]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'tt/bisect-in-c'
authorJunio C Hamano <gitster@pobox.com>
Thu, 7 Feb 2019 06:05:22 +0000 (22:05 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 7 Feb 2019 06:05:22 +0000 (22:05 -0800)
More code in "git bisect" has been rewritten in C.

* tt/bisect-in-c:
  bisect--helper: `bisect_start` shell function partially in C
  bisect--helper: `get_terms` & `bisect_terms` shell function in C
  bisect--helper: `bisect_next_check` shell function in C
  bisect--helper: `check_and_set_terms` shell function in C
  wrapper: move is_empty_file() and rename it as is_empty_or_missing_file()
  bisect--helper: `bisect_write` shell function in C
  bisect--helper: `bisect_reset` shell function in C

366 files changed:
.gitignore
.mailmap
Documentation/Makefile
Documentation/RelNotes/2.21.0.txt
Documentation/config/advice.txt
Documentation/config/http.txt
Documentation/config/rebase.txt
Documentation/config/worktree.txt
Documentation/diff-options.txt
Documentation/git-add.txt
Documentation/git-branch.txt
Documentation/git-cat-file.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-clone.txt
Documentation/git-column.txt
Documentation/git-diff.txt
Documentation/git-fast-export.txt
Documentation/git-fast-import.txt
Documentation/git-fetch.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-gc.txt
Documentation/git-help.txt
Documentation/git-init.txt
Documentation/git-instaweb.txt
Documentation/git-p4.txt
Documentation/git-push.txt
Documentation/git-quiltimport.txt
Documentation/git-rebase.txt
Documentation/git-reset.txt
Documentation/git-send-email.txt
Documentation/git-show-ref.txt
Documentation/git-status.txt
Documentation/git-tag.txt
Documentation/git-upload-pack.txt
Documentation/gitattributes.txt
Documentation/gitdiffcore.txt
Documentation/gitweb.conf.txt
Documentation/glossary-content.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/technical/api-oid-array.txt
Documentation/technical/commit-graph-format.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/partial-clone.txt
Documentation/technical/protocol-v2.txt
Makefile
advice.c
advice.h
alias.c
alloc.c
alloc.h
apply.c
archive-tar.c
archive.c
attr.c
banned.h
bisect.c
bisect.h
blame.c
branch.c
branch.h
builtin/add.c
builtin/am.c
builtin/archive.c
builtin/bisect--helper.c
builtin/branch.c
builtin/bundle.c
builtin/cat-file.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clone.c
builtin/commit-graph.c
builtin/commit.c
builtin/config.c
builtin/count-objects.c
builtin/difftool.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/init-db.c
builtin/log.c
builtin/ls-files.c
builtin/ls-tree.c
builtin/merge-ours.c
builtin/merge-recursive.c
builtin/merge-tree.c
builtin/merge.c
builtin/notes.c
builtin/pack-objects.c
builtin/prune.c
builtin/pull.c
builtin/push.c
builtin/read-tree.c
builtin/rebase--interactive.c
builtin/rebase.c
builtin/receive-pack.c
builtin/reflog.c
builtin/repack.c
builtin/rerere.c
builtin/reset.c
builtin/rev-list.c
builtin/revert.c
builtin/send-pack.c
builtin/stripspace.c
builtin/submodule--helper.c
builtin/worktree.c
bundle.c
bundle.h
cache-tree.c
cache-tree.h
cache.h
combine-diff.c
commit-graph.c
commit-graph.h
commit-reach.c
commit-reach.h
commit.c
commit.h
compat/cygwin.c [deleted file]
compat/cygwin.h [deleted file]
compat/mingw.c
compat/mingw.h
compat/obstack.c
compat/obstack.h
compat/precompose_utf8.c
compat/regex/regcomp.c
compat/win32/path-utils.c [new file with mode: 0644]
compat/win32/path-utils.h [new file with mode: 0644]
config.mak.dev
config.mak.uname
connect.c
contrib/coccinelle/object_id.cocci
contrib/coccinelle/strbuf.cocci
contrib/coccinelle/the_repository.pending.cocci [new file with mode: 0644]
contrib/completion/git-completion.bash
contrib/completion/git-completion.zsh
contrib/hooks/multimail/CHANGES
contrib/hooks/multimail/CONTRIBUTING.rst
contrib/hooks/multimail/README.Git
contrib/hooks/multimail/README.rst [moved from contrib/hooks/multimail/README with 95% similarity]
contrib/hooks/multimail/doc/gitolite.rst
contrib/hooks/multimail/git_multimail.py
contrib/hooks/multimail/migrate-mailhook-config
contrib/hooks/multimail/post-receive.example
convert.c
credential-cache--daemon.c
delta-islands.c
delta-islands.h
diff-lib.c
diff.c
diff.h
diffcore-pickaxe.c
dir.c
entry.c
environment.c
fast-import.c
fetch-pack.c
fsck.c
fuzz-commit-graph.c [new file with mode: 0644]
git-compat-util.h
git-instaweb.sh
git-legacy-rebase.sh
git-p4.py
git-quiltimport.sh
git-rebase--am.sh
git-rebase--common.sh
git-rebase--merge.sh [deleted file]
git-stash.sh
git.c
grep.c
hash.h
hex.c
http-backend.c
http-push.c
http-walker.c
http.c
http.h
imap-send.c
line-log.c
list-objects-filter-options.c
list-objects-filter-options.h
list-objects-filter.c
list-objects-filter.h
list-objects.c
ll-merge.c
ll-merge.h
log-tree.c
ls-refs.c
match-trees.c
merge-recursive.c
midx.c
notes-cache.c
notes-cache.h
notes-merge.c
notes-merge.h
notes.c
object-store.h
object.c
pack-bitmap-write.c
pack-bitmap.c
pack-bitmap.h
pack-check.c
pack-objects.c
pack-objects.h
pack.h
packfile.c
packfile.h
parse-options-cb.c
parse-options.c
parse-options.h
path.c
path.h
pathspec.c
pathspec.h
pkt-line.c
pkt-line.h
pretty.c
pretty.h
quote.c
read-cache.c
rebase-interactive.c
rebase-interactive.h
ref-filter.c
remote-curl.c
remote.c
repository.c
repository.h
rerere.c
rerere.h
revision.c
revision.h
send-pack.c
sequencer.c
sequencer.h
serve.c
setup.c
sha1-array.c
sha1-array.h
sha1-file.c
sha1-name.c
sha256/block/sha256.c [new file with mode: 0644]
sha256/block/sha256.h [new file with mode: 0644]
sha256/gcrypt.h [new file with mode: 0644]
shallow.c
sideband.c
sideband.h
streaming.c
string-list.c
submodule.c
submodule.h
symlinks.c
t/README
t/check-non-portable-shell.pl
t/helper/test-hash-speed.c [new file with mode: 0644]
t/helper/test-hash.c [new file with mode: 0644]
t/helper/test-ref-store.c
t/helper/test-repository.c
t/helper/test-sha1.c
t/helper/test-sha256.c [new file with mode: 0644]
t/helper/test-sigchain.c
t/helper/test-submodule-nested-repo-config.c
t/helper/test-tool.c
t/helper/test-tool.h
t/lib-git-daemon.sh
t/lib-git-p4.sh
t/lib-git-svn.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/lib-submodule-update.sh
t/t0003-attributes.sh
t/t0015-hash.sh [new file with mode: 0755]
t/t0025-crlf-renormalize.sh
t/t0027-auto-crlf.sh
t/t0028-working-tree-encoding.sh
t/t0030-stripspace.sh
t/t0040-parse-options.sh
t/t0061-run-command.sh
t/t0410-partial-clone.sh
t/t1410-reflog.sh
t/t1450-fsck.sh
t/t1512-rev-parse-disambiguation.sh
t/t2018-checkout-branch.sh
t/t2024-checkout-dwim.sh
t/t2028-worktree-move.sh
t/t3404-rebase-interactive.sh
t/t3406-rebase-message.sh
t/t3418-rebase-continue.sh
t/t3420-rebase-autostash.sh
t/t3421-rebase-topology-linear.sh
t/t3425-rebase-topology-merges.sh
t/t3430-rebase-merges.sh
t/t3502-cherry-pick-merge.sh
t/t3506-cherry-pick-ff.sh
t/t3510-cherry-pick-sequence.sh
t/t3700-add.sh
t/t3903-stash.sh
t/t4006-diff-mode.sh
t/t4013-diff-various.sh
t/t4013/diff.diff-tree_--cc_--shortstat_master [new file with mode: 0644]
t/t4013/diff.diff-tree_--cc_--summary_REVERSE [new file with mode: 0644]
t/t4013/diff.diff_--dirstat_--cc_master~1_master [new file with mode: 0644]
t/t4015-diff-whitespace.sh
t/t4066-diff-emit-delay.sh [new file with mode: 0755]
t/t4205-log-pretty-formats.sh
t/t4209-log-pickaxe.sh
t/t4211-line-log.sh
t/t5004-archive-corner-cases.sh
t/t5318-commit-graph.sh
t/t5403-post-checkout-hook.sh
t/t5407-post-rewrite-hook.sh
t/t5409-colorize-remote-messages.sh
t/t5500-fetch-pack.sh
t/t5505-remote.sh
t/t5512-ls-remote.sh
t/t5526-fetch-submodules.sh
t/t5537-fetch-shallow.sh
t/t5570-git-daemon.sh
t/t5580-clone-push-unc.sh
t/t5581-http-curl-verbose.sh [new file with mode: 0755]
t/t5601-clone.sh
t/t5611-clone-config.sh
t/t5616-partial-clone.sh
t/t5701-git-serve.sh
t/t5702-protocol-v2.sh
t/t5703-upload-pack-ref-in-want.sh
t/t6006-rev-list-format.sh
t/t6036-recursive-corner-cases.sh
t/t6042-merge-rename-corner-cases.sh
t/t6043-merge-rename-directories.sh
t/t6050-replace.sh
t/t6112-rev-list-filters-objects.sh
t/t6135-pathspec-with-attrs.sh
t/t6300-for-each-ref.sh
t/t7400-submodule-basic.sh
t/t7412-submodule-absorbgitdirs.sh
t/t7415-submodule-names.sh
t/t7505-prepare-commit-msg-hook.sh
t/t9350-fast-export.sh
t/t9807-git-p4-submit.sh
t/t9902-completion.sh
t/t9903-bash-prompt.sh
t/test-lib-functions.sh
t/test-lib.sh
transport-helper.c
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
tree.h
unpack-trees.c
upload-pack.c
url.c
userdiff.c
userdiff.h
utf8.c
utf8.h
walker.c
wt-status.c
wt-status.h

index 0d77ea5894274c43c4b348c8b52b8e665a1a339e..6fd61c7272fb460c50cc0b1e3a08ce97a6cf912f 100644 (file)
@@ -1,3 +1,4 @@
+/fuzz-commit-graph
 /fuzz_corpora
 /fuzz-pack-headers
 /fuzz-pack-idx
 /git-rebase--am
 /git-rebase--common
 /git-rebase--interactive
-/git-rebase--merge
 /git-rebase--preserve-merges
 /git-receive-pack
 /git-reflog
index eb7b5fc7b97f33999eced124943728854bc2e983..247a3deb7e1418f0fdcfd9719cb7f609775d2804 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -27,6 +27,7 @@ Ben Walton <bdwalton@gmail.com> <bwalton@artsci.utoronto.ca>
 Benoit Sigoure <tsunanet@gmail.com> <tsuna@lrde.epita.fr>
 Bernt Hansen <bernt@norang.ca> <bernt@alumni.uwaterloo.ca>
 Brandon Casey <drafnel@gmail.com> <casey@nrlssc.navy.mil>
+Brandon Williams <bwilliams.eng@gmail.com> <bmwill@google.com>
 brian m. carlson <sandals@crustytoothpaste.net>
 brian m. carlson <sandals@crustytoothpaste.net> <sandals@crustytoothpaste.ath.cx>
 Bryan Larsen <bryan@larsen.st> <bryan.larsen@gmail.com>
index d5d936e6a7a3dd8839901a0dd606cdc813249c1c..26a2342beaf0132241980dbbcaf8592fd4d183dc 100644 (file)
@@ -36,14 +36,19 @@ MAN7_TXT += gittutorial-2.txt
 MAN7_TXT += gittutorial.txt
 MAN7_TXT += gitworkflows.txt
 
+ifdef MAN_FILTER
+MAN_TXT = $(filter $(MAN_FILTER),$(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT))
+else
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
+MAN_FILTER = $(MAN_TXT)
+endif
+
 MAN_XML = $(patsubst %.txt,%.xml,$(MAN_TXT))
 MAN_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
 GIT_MAN_REF = master
 
 OBSOLETE_HTML += everyday.html
 OBSOLETE_HTML += git-remote-helpers.html
-DOC_HTML = $(MAN_HTML) $(OBSOLETE_HTML)
 
 ARTICLES += howto-index
 ARTICLES += git-tools
@@ -89,11 +94,13 @@ TECH_DOCS += technical/trivial-merge
 SP_ARTICLES += $(TECH_DOCS)
 SP_ARTICLES += technical/api-index
 
-DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
+ARTICLES_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
+HTML_FILTER ?= $(ARTICLES_HTML) $(OBSOLETE_HTML)
+DOC_HTML = $(MAN_HTML) $(filter $(HTML_FILTER),$(ARTICLES_HTML) $(OBSOLETE_HTML))
 
-DOC_MAN1 = $(patsubst %.txt,%.1,$(MAN1_TXT))
-DOC_MAN5 = $(patsubst %.txt,%.5,$(MAN5_TXT))
-DOC_MAN7 = $(patsubst %.txt,%.7,$(MAN7_TXT))
+DOC_MAN1 = $(patsubst %.txt,%.1,$(filter $(MAN_FILTER),$(MAN1_TXT)))
+DOC_MAN5 = $(patsubst %.txt,%.5,$(filter $(MAN_FILTER),$(MAN5_TXT)))
+DOC_MAN7 = $(patsubst %.txt,%.7,$(filter $(MAN_FILTER),$(MAN7_TXT)))
 
 prefix ?= $(HOME)
 bindir ?= $(prefix)/bin
@@ -457,4 +464,9 @@ print-man1:
 lint-docs::
        $(QUIET_LINT)$(PERL_PATH) lint-gitlink.perl
 
+ifeq ($(wildcard po/Makefile),po/Makefile)
+doc-l10n install-l10n::
+       $(MAKE) -C po $@
+endif
+
 .PHONY: FORCE
index ad681bd5d54dcd30dac5ffe2a173cd745a67065c..5ad2233c9950b1b3d0c3b558ac9c81bb3b7cf2f0 100644 (file)
@@ -10,13 +10,323 @@ Updates since v2.20
 
 UI, Workflows & Features
 
+ * The "http.version" configuration variable can be used with recent
+   enough cURL library to force the version of HTTP used to talk when
+   fetching and pushing.
+
+ * Small fixes and features for fast-export and fast-import, mostly on
+   the fast-export side.
+
+ * "git push $there $src:$dst" rejects when $dst is not a fully
+   qualified refname and not clear what the end user meant.  The
+   codepath has been taught to give a clearer error message, and also
+   guess where the push should go by taking the type of the pushed
+   object into account (e.g. a tag object would want to go under
+   refs/tags/).
+
+ * "git checkout [<tree-ish>] path..." learned to report the number of
+   paths that have been checked out of the index or the tree-ish,
+   which gives it the same degree of noisy-ness as the case in which
+   the command checks out a branch.
+
+ * "git quiltimport" learned "--keep-non-patch" option.
+
+ * "git worktree remove" and "git worktree move" refused to work when
+   there is a submodule involved.  This has been loosened to ignore
+   uninitialized submodules.
+
+ * "git cherry-pick -m1" was forbidden when picking a non-merge
+   commit, even though there _is_ parent number 1 for such a commit.
+   This was done to avoid mistakes back when "cherry-pick" was about
+   picking a single commit, but is no longer useful with "cherry-pick"
+   that can pick a range of commits.  Now the "-m$num" option is
+   allowed when picking any commit, as long as $num names an existing
+   parent of the commit.
+
+ * Update "git multimail" from the upstream.
+
+ * "git p4" update.
+
+ * The "--format=<placeholder>" option of for-each-ref, branch and tag
+   learned to show a few more traits of objects that can be learned by
+   the object_info API.
+
+ * "git rebase -i" learned to re-execute a command given with 'exec'
+   to run after it failed the last time.
+
+ * "git diff --color-moved-ws" updates.
+
+ * Custom userformat "log --format" learned %S atom that stands for
+   the tip the traversal reached the commit from, i.e. --source.
+
+ * "git instaweb" learned to drive http.server that comes with
+   "batteries included" Python installation (both Python2 & 3).
 
 
 Performance, Internal Implementation, Development Support etc.
 
+ * Code clean-up with optimization for the codepath that checks
+   (non-)existence of loose objects.
+
+ * More codepaths become aware of working with in-core repository
+   instance other than the default "the_repository".
+
+ * The "strncat()" function is now among the banned functions.
+
+ * Portability updates for the HPE NonStop platform.
+
+ * Earlier we added "-Wformat-security" to developer builds, assuming
+   that "-Wall" (which includes "-Wformat" which in turn is required
+   to use "-Wformat-security") is always in effect.  This is not true
+   when config.mak.autogen is in use, unfortunately.  This has been
+   fixed by unconditionally adding "-Wall" to developer builds.
+
+ * The loose object cache used to optimize existence look-up has been
+   updated.
+
+ * Flaky tests can now be repeatedly run under load with the
+   "--stress" option.
+   (merge fb7d1e3ac8 sg/stress-test later to maint).
+
+ * Documentation/Makefile is getting prepared for manpage
+   localization.
+
+ * "git fetch-pack" now can talk the version 2 protocol.
+
+ * sha-256 hash has been added and plumbed through the code to allow
+   building Git with the "NewHash".
+
+ * Debugging help for http transport.
+
+ * "git fetch --deepen=<more>" has been corrected to work over v2
+   protocol.
+
+ * The code to walk tree objects has been taught that we may be
+   working with object names that are not computed with SHA-1.
+
+ * The in-core repository instances are passed through more codepaths.
+
+ * Update the protocol message specification to allow only the limited
+   use of scaled quantities.  This is ensure potential compatibility
+   issues will not go out of hand.
+
+ * Micro-optimize the code that prepares commit objects to be walked
+   by "git rev-list" when the commit-graph is available.
+
+ * "git fetch" and "git upload-pack" learned to send all exchange over
+   the sideband channel while talking the v2 protocol.
+
+ * The codepath to write out commit-graph has been optimized by
+   following the usual pattern of visiting objects in in-pack order.
+
+ * The codepath to show progress meter while writing out commit-graph
+   file has been improved.
+
+ * Cocci rules have been updated to encourage use of strbuf_addbuf().
 
 
 Fixes since v2.20
 -----------------
 
+ * Updates for corner cases in merge-recursive.
+   (merge cc4cb0902c en/merge-path-collision later to maint).
+
+ * "git checkout frotz" (without any double-dash) avoids ambiguity by
+   making sure 'frotz' cannot be interpreted as a revision and as a
+   path at the same time.  This safety has been updated to check also
+   a unique remote-tracking branch 'frotz' in a remote, when dwimming
+   to create a local branch 'frotz' out of a remote-tracking branch
+   'frotz' from a remote.
+   (merge be4908f103 nd/checkout-dwim-fix later to maint).
+
+ * Refspecs configured with "git -c var=val clone" did not propagate
+   to the resulting repository, which has been corrected.
+   (merge 7eae4a3ac4 sg/clone-initial-fetch-configuration later to maint).
+
+ * A properly configured username/email is required under
+   user.useConfigOnly in order to create commits; now "git stash"
+   (even though it creates commit objects to represent stash entries)
+   command is exempt from the requirement.
+   (merge 3bc2111fc2 sd/stash-wo-user-name later to maint).
+
+ * The http-backend CGI process did not correctly clean up the child
+   processes it spawns to run upload-pack etc. when it dies itself,
+   which has been corrected.
+   (merge 02818a98d7 mk/http-backend-kill-children-before-exit later to maint).
+
+ * "git rev-list --exclude-promisor-objects" had to take an object
+   that does not exist locally (and is lazily available) from the
+   command line without barfing, but the code dereferenced NULL.
+   (merge 4cf67869b2 md/list-lazy-objects-fix later to maint).
+
+ * The traversal over tree objects has learned to honor
+   ":(attr:label)" pathspec match, which has been implemented only for
+   enumerating paths on the filesystem.
+   (merge 5a0b97b34c nd/attr-pathspec-in-tree-walk later to maint).
+
+ * BSD port updates.
+   (merge 4e3ecbd439 cb/openbsd-allows-reading-directory later to maint).
+   (merge b6bdc2a0f5 cb/t5004-empty-tar-archive-fix later to maint).
+   (merge 82cbc8cde2 cb/test-lint-cp-a later to maint).
+
+ * Lines that begin with a certain keyword that come over the wire, as
+   well as lines that consist only of one of these keywords, ought to
+   be painted in color for easier eyeballing, but the latter was
+   broken ever since the feature was introduced in 2.19, which has
+   been corrected.
+   (merge 1f67290450 hn/highlight-sideband-keywords later to maint).
+
+ * "git log -G<regex>" looked for a hunk in the "git log -p" patch
+   output that contained a string that matches the given pattern.
+   Optimize this code to ignore binary files, which by default will
+   not show any hunk that would match any pattern (unless textconv or
+   the --text option is in effect, that is).
+   (merge e0e7cb8080 tb/log-G-binary later to maint).
+
+ * "git submodule update" ought to use a single job unless asked, but
+   by mistake used multiple jobs, which has been fixed.
+   (merge e3a9d1aca9 sb/submodule-fetchjobs-default-to-one later to maint).
+
+ * "git stripspace" should be usable outside a git repository, but
+   under the "-s" or "-c" mode, it didn't.
+   (merge 957da75802 jn/stripspace-wo-repository later to maint).
+
+ * Some of the documentation pages formatted incorrectly with
+   Asciidoctor, which have been fixed.
+   (merge b62eb1d2f4 ma/asciidoctor later to maint).
+
+ * The core.worktree setting in a submodule repository should not be
+   pointing at a directory when the submodule loses its working tree
+   (e.g. getting deinit'ed), but the code did not properly maintain
+   this invariant.
+
+ * With zsh, "git cmd path<TAB>" was completed to "git cmd path name"
+   when the completed path has a special character like SP in it,
+   without any attempt to keep "path name" a single filename.  This
+   has been fixed to complete it to "git cmd path\ name" just like
+   Bash completion does.
+
+ * The test suite tried to see if it is run under bash, but the check
+   itself failed under some other implementations of shell (notably
+   under NetBSD).  This has been corrected.
+   (merge 54ea72f09c sg/test-bash-version-fix later to maint).
+
+ * "git gc" and "git repack" did not close the open packfiles that
+   they found unneeded before removing them, which didn't work on a
+   platform incapable of removing an open file.  This has been
+   corrected.
+   (merge 5bdece0d70 js/gc-repack-close-before-remove later to maint).
+
+ * The code to drive GIT_EXTERNAL_DIFF command relied on the string
+   returned from getenv() to be non-volatile, which is not true, that
+   has been corrected.
+   (merge 6776a84dae kg/external-diff-save-env later to maint).
+
+ * There were many places the code relied on the string returned from
+   getenv() to be non-volatile, which is not true, that have been
+   corrected.
+   (merge 0da0e9268b jk/save-getenv-result later to maint).
+
+ * The v2 upload-pack protocol implementation failed to honor
+   hidden-ref configuration, which has been corrected.
+   (merge e20b4192a3 jk/proto-v2-hidden-refs-fix later to maint).
+
+ * "git fetch --recurse-submodules" may not fetch the necessary commit
+   that is bound to the superproject, which is getting corrected.
+   (merge be76c21282 sb/submodule-recursive-fetch-gets-the-tip later to maint).
+
+ * "git rebase" internally runs "checkout" to switch between branches,
+   and the command used to call the post-checkout hook, but the
+   reimplementation stopped doing so, which is getting fixed.
+
+ * "git add -e" got confused when the change it wants to let the user
+   edit is smaller than the previous change that was left over in a
+   temporary file.
+   (merge fa6f225e01 js/add-e-clear-patch-before-stating later to maint).
+
+ * "git p4" failed to update a shelved change when there were moved
+   files, which has been corrected.
+   (merge 7a10946ab9 ld/git-p4-shelve-update-fix later to maint).
+
+ * The codepath to read from the commit-graph file attempted to read
+   past the end of it when the file's table-of-contents was corrupt.
+
+ * The compat/obstack code had casts that -Wcast-function-type
+   compilation option found questionable.
+   (merge 764473d257 sg/obstack-cast-function-type-fix later to maint).
+
+ * An obvious typo in an assertion error message has been fixed.
+   (merge 3c27e2e059 cc/test-ref-store-typofix later to maint).
+
+ * In Git for Windows, "git clone \\server\share\path" etc. that uses
+   UNC paths from command line had bad interaction with its shell
+   emulation.
+
+ * "git add --ignore-errors" did not work as advertised and instead
+   worked as an unintended synonym for "git add --renormalize", which
+   has been fixed.
+   (merge 9e5da3d055 jk/add-ignore-errors-bit-assignment-fix later to maint).
+
+ * On a case-insensitive filesystem, we failed to compare the part of
+   the path that is above the worktree directory in an absolute
+   pathname, which has been corrected.
+
+ * Asking "git check-attr" about a macro (e.g. "binary") on a specific
+   path did not work correctly, even though "git check-attr -a" listed
+   such a macro correctly.  This has been corrected.
+   (merge 7b95849be4 jk/attr-macro-fix later to maint).
+
+ * "git pack-objects" incorrectly used uninitialized mutex, which has
+   been corrected.
+   (merge edb673cf10 ph/pack-objects-mutex-fix later to maint).
+
+ * "git checkout -b <new> [HEAD]" to create a new branch from the
+   current commit and check it out ought to be a no-op in the index
+   and the working tree in normal cases, but there are corner cases
+   that do require updates to the index and the working tree.  Running
+   it immediately after "git clone --no-checkout" is one of these
+   cases that an earlier optimization kicked in incorrectly, which has
+   been fixed.
+   (merge 8424bfd45b bp/checkout-new-branch-optim later to maint).
+
+ * "git diff --color-moved --cc --stat -p" did not work well due to
+   funny interaction between a bug in color-moved and the rest, which
+   has been fixed.
+   (merge dac03b5518 jk/diff-cc-stat-fixes later to maint).
+
+ * When GIT_SEQUENCE_EDITOR is set, the command was incorrectly
+   started when modes of "git rebase" that implicitly uses the
+   machinery for the interactive rebase are run, which has been
+   corrected.
+   (merge 891d4a0313 pw/no-editor-in-rebase-i-implicit later to maint).
+
+ * The commit-graph facility did not work when in-core objects that
+   are promoted from unknown type to commit (e.g. a commit that is
+   accessed via a tag that refers to it) were involved, which has been
+   corrected.
+   (merge 4468d4435c sg/object-as-type-commit-graph-fix later to maint).
+
+ * "git fetch" output cleanup.
+   (merge dc40b24df4 nd/fetch-compact-update later to maint).
+
  * Code cleanup, docfix, build fix, etc.
+   (merge 89ba9a79ae hb/t0061-dot-in-path-fix later to maint).
+   (merge d173e799ea sb/diff-color-moved-config-option-fixup later to maint).
+   (merge a8f5a59067 en/directory-renames-nothanks-doc-update later to maint).
+   (merge ec36c42a63 nd/indentation-fix later to maint).
+   (merge f116ee21cd do/gitweb-strict-export-conf-doc later to maint).
+   (merge 112ea42663 fd/gitweb-snapshot-conf-doc-fix later to maint).
+   (merge 1cadad6f65 tb/use-common-win32-pathfuncs-on-cygwin later to maint).
+   (merge 57e9dcaa65 km/rebase-doc-typofix later to maint).
+   (merge b8b4cb27e6 ds/gc-doc-typofix later to maint).
+   (merge 3b3357626e nd/style-opening-brace later to maint).
+   (merge b4583d5595 es/doc-worktree-guessremote-config later to maint).
+   (merge cce99cd8c6 ds/commit-graph-assert-missing-parents later to maint).
+   (merge 0650614982 cy/completion-typofix later to maint).
+   (merge 6881925ef5 rs/sha1-file-close-mapped-file-on-error later to maint).
+   (merge bd8d6f0def en/show-ref-doc-fix later to maint).
+   (merge 1747125e2c cc/parial-clone-doc-typofix later to maint).
+   (merge e01378753d cc/fetch-error-message-fix later to maint).
+   (merge 54e8c11215 jk/remote-insteadof-cleanup later to maint).
+   (merge d609615f48 js/test-git-installed later to maint).
+   (merge ba170517be ja/doc-style-fix later to maint).
index 57fcd4c8622fc6e996308a1412a27100419a16be..88620429eacf857c7ba7941314c9b443b35b58ce 100644 (file)
@@ -30,6 +30,13 @@ advice.*::
                tries to overwrite a remote ref that points at an
                object that is not a commit-ish, or make the remote
                ref point at an object that is not a commit-ish.
+       pushUnqualifiedRefname::
+               Shown when linkgit:git-push[1] gives up trying to
+               guess based on the source and destination refs what
+               remote ref namespace the source belongs in, but where
+               we can still suggest that the user push to either
+               refs/heads/* or refs/tags/* based on the type of the
+               source object.
        statusHints::
                Show directions on how to proceed from the current
                state in the output of linkgit:git-status[1], in
index a56d848bc0ff87e4fed52081bd7d92952c319c68..5a32f5b0a5a9c312bb414d0405cab0f97e826a01 100644 (file)
@@ -68,6 +68,15 @@ http.saveCookies::
        If set, store cookies received during requests to the file specified by
        http.cookieFile. Has no effect if http.cookieFile is unset.
 
+http.version::
+       Use the specified HTTP protocol version when communicating with a server.
+       If you want to force the default. The available and default version depend
+       on libcurl. Actually the possible values of
+       this option are:
+
+       - HTTP/2
+       - HTTP/1.1
+
 http.sslVersion::
        The SSL version to use when negotiating an SSL connection, if you
        want to force the default.  The available and default version
index f079bf6b7e5d88e012074a223a785f8de5949f78..331d250e0468df8ec51bdb3348d6a30274da26ed 100644 (file)
@@ -64,3 +64,8 @@ instead of:
 -------------------------------------------
 +
 Defaults to false.
+
+rebase.rescheduleFailedExec::
+       Automatically reschedule `exec` commands that failed. This only makes
+       sense in interactive mode (or when an `--exec` option was provided).
+       This is the same as specifying the `--reschedule-failed-exec` option.
index b853798fc2b159b13923be63f4068064f04579ce..048e349482df6c892055720eb53cdcd6c327b6ed 100644 (file)
@@ -1,6 +1,6 @@
 worktree.guessRemote::
-       With `add`, if no branch argument, and neither of `-b` nor
-       `-B` nor `--detach` are given, the command defaults to
+       If no branch is specified and neither `-b` nor `-B` nor
+       `--detach` is used, then `git worktree add` defaults to
        creating a new branch from HEAD.  If `worktree.guessRemote` is
        set to true, `worktree add` tries to find a remote-tracking
        branch whose name uniquely matches the new branch name.  If
index 0378cd574eb01069baba14221d511f580565c0eb..554a34080d7081da917cd54cd34eceb7bf4cd95c 100644 (file)
@@ -293,8 +293,12 @@ dimmed-zebra::
        `dimmed_zebra` is a deprecated synonym.
 --
 
+--no-color-moved::
+       Turn off move detection. This can be used to override configuration
+       settings. It is the same as `--color-moved=no`.
+
 --color-moved-ws=<modes>::
-       This configures how white spaces are ignored when performing the
+       This configures how whitespace is ignored when performing the
        move detection for `--color-moved`.
 ifdef::git-diff[]
        It can be set by the `diff.colorMovedWS` configuration setting.
@@ -302,6 +306,8 @@ endif::git-diff[]
        These modes can be given as a comma separated list:
 +
 --
+no::
+       Do not ignore whitespace when performing move detection.
 ignore-space-at-eol::
        Ignore changes in whitespace at EOL.
 ignore-space-change::
@@ -312,12 +318,17 @@ ignore-all-space::
        Ignore whitespace when comparing lines. This ignores differences
        even if one line has whitespace where the other line has none.
 allow-indentation-change::
-       Initially ignore any white spaces in the move detection, then
+       Initially ignore any whitespace in the move detection, then
        group the moved code blocks only into a block if the change in
        whitespace is the same per line. This is incompatible with the
        other modes.
 --
 
+--no-color-moved-ws::
+       Do not ignore whitespace when performing move detection. This can be
+       used to override configuration settings. It is the same as
+       `--color-moved-ws=no`.
+
 --word-diff[=<mode>]::
        Show a word diff, using the <mode> to delimit changed words.
        By default, words are delimited by whitespace; see
@@ -524,6 +535,8 @@ struct), and want to know the history of that block since it first
 came into being: use the feature iteratively to feed the interesting
 block in the preimage back into `-S`, and keep going until you get the
 very first version of the block.
++
+Binary files are searched as well.
 
 -G<regex>::
        Look for differences whose patch text contains added/removed
@@ -543,6 +556,9 @@ While `git log -G"regexec\(regexp"` will show this commit, `git log
 -S"regexec\(regexp" --pickaxe-regex` will not (because the number of
 occurrences of that string did not change).
 +
+Unless `--text` is supplied patches of binary files without a textconv
+filter will be ignored.
++
 See the 'pickaxe' entry in linkgit:gitdiffcore[7] for more
 information.
 
index 45652fe4a6a51c251f773776576956745701d677..37bcab94d5e435849c22ddd0473be5cfb1c56140 100644 (file)
@@ -58,9 +58,9 @@ OPTIONS
        specifying `dir` will record not just a file `dir/file1`
        modified in the working tree, a file `dir/file2` added to
        the working tree, but also a file `dir/file3` removed from
-       the working tree Note that older versions of Git used
+       the working tree). Note that older versions of Git used
        to ignore removed files; use `--no-all` option if you want
-       to add modified or new files but ignore removed ones.
+       to add modified or new files but ignore removed ones.
 +
 For more details about the <pathspec> syntax, see the 'pathspec' entry
 in linkgit:gitglossary[7].
@@ -124,7 +124,7 @@ subdirectories).
 --no-ignore-removal::
        Update the index not only where the working tree has a file
        matching <pathspec> but also where the index already has an
-       entry.  This adds, modifies, and removes index entries to
+       entry. This adds, modifies, and removes index entries to
        match the working tree.
 +
 If no <pathspec> is given when `-A` option is used, all
@@ -206,7 +206,7 @@ EXAMPLES
 --------
 
 * Adds content from all `*.txt` files under `Documentation` directory
-and its subdirectories:
+  and its subdirectories:
 +
 ------------
 $ git add Documentation/\*.txt
index bf5316ffa929a88aa98f5b3c7a892ee43638a098..3bd83a7cbdd9d62c9b10057bd4bf3f03d57b1085 100644 (file)
@@ -297,7 +297,7 @@ $ git checkout my2.6.14
 ------------
 +
 <1> This step and the next one could be combined into a single step with
-"checkout -b my2.6.14 v2.6.14".
+    "checkout -b my2.6.14 v2.6.14".
 
 Delete an unneeded branch::
 +
@@ -309,10 +309,10 @@ $ git branch -D test                                    <2>
 ------------
 +
 <1> Delete the remote-tracking branches "todo", "html" and "man". The next
-'fetch' or 'pull' will create them again unless you configure them not to.
-See linkgit:git-fetch[1].
+    'fetch' or 'pull' will create them again unless you configure them not to.
+    See linkgit:git-fetch[1].
 <2> Delete the "test" branch even if the "master" branch (or whichever branch
-is currently checked out) does not have all commits from the test branch.
+    is currently checked out) does not have all commits from the test branch.
 
 
 NOTES
index 74013335a1ccd18228a53acbd8dd7f5fe1ab40ca..8eca671b8278cfe02692605b80a9121bc5717567 100644 (file)
@@ -23,8 +23,8 @@ In the second form, a list of objects (separated by linefeeds) is provided on
 stdin, and the SHA-1, type, and size of each object is printed on stdout. The
 output format can be overridden using the optional `<format>` argument. If
 either `--textconv` or `--filters` was specified, the input is expected to
-list the object names followed by the path name, separated by a single white
-space, so that the appropriate drivers can be determined.
+list the object names followed by the path name, separated by a single
+whitespace, so that the appropriate drivers can be determined.
 
 OPTIONS
 -------
@@ -79,7 +79,7 @@ OPTIONS
        Print object information and contents for each object provided
        on stdin.  May not be combined with any other options or arguments
        except `--textconv` or `--filters`, in which case the input lines
-       also need to specify the path, separated by white space.  See the
+       also need to specify the path, separated by whitespace.  See the
        section `BATCH OUTPUT` below for details.
 
 --batch-check::
@@ -87,7 +87,7 @@ OPTIONS
        Print object information for each object provided on stdin.  May
        not be combined with any other options or arguments except
        `--textconv` or `--filters`, in which case the input lines also
-       need to specify the path, separated by white space.  See the
+       need to specify the path, separated by whitespace.  See the
        section `BATCH OUTPUT` below for details.
 
 --batch-all-objects::
@@ -252,6 +252,12 @@ the repository, then `cat-file` will ignore any custom format and print:
 <object> SP missing LF
 ------------
 
+If a name is specified that might refer to more than one object (an ambiguous short sha), then `cat-file` will ignore any custom format and print:
+
+------------
+<object> SP ambiguous LF
+------------
+
 If --follow-symlinks is used, and a symlink in the repository points
 outside the repository, then `cat-file` will ignore any custom format
 and print:
index 801de2f7645bf0b1fc506da58d469519abca8863..9a396498d106e1f3ff581d465c072d90686f8c21 100644 (file)
@@ -276,6 +276,10 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
        Just like linkgit:git-submodule[1], this will detach the
        submodules HEAD.
 
+--no-guess::
+       Do not attempt to create a branch if a remote tracking branch
+       of the same name exists.
+
 <branch>::
        Branch to checkout; if it refers to a branch (i.e., a name that,
        when prepended with "refs/heads/", is a valid ref), then that
@@ -420,14 +424,14 @@ $ git tag foo           <3>
 ------------
 
 <1> creates a new branch 'foo', which refers to commit 'f', and then
-updates HEAD to refer to branch 'foo'. In other words, we'll no longer
-be in detached HEAD state after this command.
+    updates HEAD to refer to branch 'foo'. In other words, we'll no longer
+    be in detached HEAD state after this command.
 
 <2> similarly creates a new branch 'foo', which refers to commit 'f',
-but leaves HEAD detached.
+    but leaves HEAD detached.
 
 <3> creates a new tag 'foo', which refers to commit 'f',
-leaving HEAD detached.
+    leaving HEAD detached.
 
 If we have moved away from commit 'f', then we must first recover its object
 name (typically by using git reflog), and then we can create a reference to
@@ -455,8 +459,8 @@ EXAMPLES
 --------
 
 . The following sequence checks out the `master` branch, reverts
-the `Makefile` to two revisions back, deletes hello.c by
-mistake, and gets it back from the index.
+  the `Makefile` to two revisions back, deletes hello.c by
+  mistake, and gets it back from the index.
 +
 ------------
 $ git checkout master             <1>
@@ -490,7 +494,7 @@ $ git checkout -- hello.c
 ------------
 
 . After working in the wrong branch, switching to the correct
-branch would be done using:
+  branch would be done using:
 +
 ------------
 $ git checkout mytopic
@@ -518,7 +522,7 @@ registered in your index file, so `git diff` would show you what
 changes you made since the tip of the new branch.
 
 . When a merge conflict happens during switching branches with
-the `-m` option, you would see something like this:
+  the `-m` option, you would see something like this:
 +
 ------------
 $ git checkout -m mytopic
index d35d771fc8172e12958b4a9521c51a5f65cddebe..b8cfeec67e5fbdcfe1705a3b249f97b284932e79 100644 (file)
@@ -213,16 +213,16 @@ $ git reset --merge ORIG_HEAD        <3>
 $ git cherry-pick -Xpatience topic^  <4>
 ------------
 <1> apply the change that would be shown by `git show topic^`.
-In this example, the patch does not apply cleanly, so
-information about the conflict is written to the index and
-working tree and no new commit results.
+    In this example, the patch does not apply cleanly, so
+    information about the conflict is written to the index and
+    working tree and no new commit results.
 <2> summarize changes to be reconciled
 <3> cancel the cherry-pick.  In other words, return to the
-pre-cherry-pick state, preserving any local modifications you had in
-the working tree.
+    pre-cherry-pick state, preserving any local modifications
+    you had in the working tree.
 <4> try to apply the change introduced by `topic^` again,
-spending extra time to avoid mistakes based on incorrectly matching
-context lines.
+    spending extra time to avoid mistakes based on incorrectly
+    matching context lines.
 
 SEE ALSO
 --------
index a55536f0bfb2dfa3774e1b952ac4dee1eea6e505..2fd12524f95afef9c688d3402cd2a8b6d2d98c2e 100644 (file)
@@ -189,6 +189,12 @@ objects from the source repository into a pack in the cloned repository.
        values are given for the same key, each value will be written to
        the config file. This makes it safe, for example, to add
        additional fetch refspecs to the origin remote.
++
+Due to limitations of the current implementation, some configuration
+variables do not take effect until after the initial fetch and checkout.
+Configuration variables known to not take effect are:
+`remote.<name>.mirror` and `remote.<name>.tagOpt`.  Use the
+corresponding `--mirror` and `--no-tags` options instead.
 
 --depth <depth>::
        Create a 'shallow' clone with a history truncated to the
index 763afabb6dc7487009ab49f5bb258f9181f0da78..f58e9c43e60cec3b56fb84f54859f3d01d1a963a 100644 (file)
@@ -47,7 +47,7 @@ OPTIONS
        The number of spaces between columns. One space by default.
 
 EXAMPLES
-------
+--------
 
 Format data by columns:
 ------------
index 030f162f301608c329fecae04953620eb78f0b14..72179d993cb9b68766e2d9093f323cb3720e38f4 100644 (file)
@@ -132,9 +132,9 @@ $ git diff HEAD       <3>
 +
 <1> Changes in the working tree not yet staged for the next commit.
 <2> Changes between the index and your last commit; what you
-would be committing if you run "git commit" without "-a" option.
+    would be committing if you run "git commit" without "-a" option.
 <3> Changes in the working tree since your last commit; what you
-would be committing if you run "git commit -a"
+    would be committing if you run "git commit -a"
 
 Comparing with arbitrary commits::
 +
@@ -145,10 +145,10 @@ $ git diff HEAD^ HEAD      <3>
 ------------
 +
 <1> Instead of using the tip of the current branch, compare with the
-tip of "test" branch.
+    tip of "test" branch.
 <2> Instead of comparing with the tip of "test" branch, compare with
-the tip of the current branch, but limit the comparison to the
-file "test".
+    the tip of the current branch, but limit the comparison to the
+    file "test".
 <3> Compare the version before the last commit and the last commit.
 
 Comparing branches::
@@ -162,7 +162,7 @@ $ git diff topic...master  <3>
 <1> Changes between the tips of the topic and the master branches.
 <2> Same as above.
 <3> Changes that occurred on the master branch since when the topic
-branch was started off it.
+    branch was started off it.
 
 Limiting the diff output::
 +
@@ -173,9 +173,9 @@ $ git diff arch/i386 include/asm-i386   <3>
 ------------
 +
 <1> Show only modification, rename, and copy, but not addition
-or deletion.
+    or deletion.
 <2> Show only names and the nature of change, but not actual
-diff output.
+    diff output.
 <3> Limit diff output to named subtrees.
 
 Munging the diff output::
@@ -186,7 +186,7 @@ $ git diff -R                          <2>
 ------------
 +
 <1> Spend extra cycles to find renames, copies and complete
-rewrites (very expensive).
+    rewrites (very expensive).
 <2> Output diff in reverse.
 
 SEE ALSO
index ce954be5321384b55687791acb32562850a5a6fa..64c01ba91884df1ec8e49ddc8fe852f1fb2a9425 100644 (file)
@@ -110,6 +110,25 @@ marks the same across runs.
        the shape of the history and stored tree.  See the section on
        `ANONYMIZING` below.
 
+--reference-excluded-parents::
+       By default, running a command such as `git fast-export
+       master~5..master` will not include the commit master{tilde}5
+       and will make master{tilde}4 no longer have master{tilde}5 as
+       a parent (though both the old master{tilde}4 and new
+       master{tilde}4 will have all the same files).  Use
+       --reference-excluded-parents to instead have the the stream
+       refer to commits in the excluded range of history by their
+       sha1sum.  Note that the resulting stream can only be used by a
+       repository which already contains the necessary parent
+       commits.
+
+--show-original-ids::
+       Add an extra directive to the output for commits and blobs,
+       `original-oid <SHA1SUM>`.  While such directives will likely be
+       ignored by importers such as git-fast-import, it may be useful
+       for intermediary filters (e.g. for rewriting commit messages
+       which refer to older commits, or for stripping blobs by id).
+
 --refspec::
        Apply the specified refspec to each ref exported. Multiple of them can
        be specified.
@@ -119,7 +138,9 @@ marks the same across runs.
        'git rev-list', that specifies the specific objects and references
        to export.  For example, `master~10..master` causes the
        current master reference to be exported along with all objects
-       added since its 10th ancestor commit.
+       added since its 10th ancestor commit and (unless the
+       --reference-excluded-parents option is specified) all files
+       common to master{tilde}9 and master{tilde}10.
 
 EXAMPLES
 --------
index e81117d27f477591b92480352731fb0c4faf3410..43ab3b1637b50462ff62aa9c33c87184ef8ebc47 100644 (file)
@@ -40,9 +40,10 @@ OPTIONS
        not contain the old commit).
 
 --quiet::
-       Disable all non-fatal output, making fast-import silent when it
-       is successful.  This option disables the output shown by
-       --stats.
+       Disable the output shown by --stats, making fast-import usually
+       be silent when it is successful.  However, if the import stream
+       has directives intended to show user output (e.g. `progress`
+       directives), the corresponding messages will still be shown.
 
 --stats::
        Display some basic statistics about the objects fast-import has
@@ -384,6 +385,7 @@ change to the project.
 ....
        'commit' SP <ref> LF
        mark?
+       original-oid?
        ('author' (SP <name>)? SP LT <email> GT SP <when> LF)?
        'committer' (SP <name>)? SP LT <email> GT SP <when> LF
        data
@@ -740,6 +742,19 @@ New marks are created automatically.  Existing marks can be moved
 to another object simply by reusing the same `<idnum>` in another
 `mark` command.
 
+`original-oid`
+~~~~~~~~~~~~~~
+Provides the name of the object in the original source control system.
+fast-import will simply ignore this directive, but filter processes
+which operate on and modify the stream before feeding to fast-import
+may have uses for this information
+
+....
+       'original-oid' SP <object-identifier> LF
+....
+
+where `<object-identifer>` is any string not containing LF.
+
 `tag`
 ~~~~~
 Creates an annotated tag referring to a specific commit.  To create
@@ -748,6 +763,7 @@ lightweight (non-annotated) tags see the `reset` command below.
 ....
        'tag' SP <name> LF
        'from' SP <commit-ish> LF
+       original-oid?
        'tagger' (SP <name>)? SP LT <email> GT SP <when> LF
        data
 ....
@@ -822,6 +838,7 @@ assigned mark.
 ....
        'blob' LF
        mark?
+       original-oid?
        data
 ....
 
index e3199355978880b21668bc1aff0e42f610c02bd6..266d63cf111c8a73420c5fa945543384e9825b55 100644 (file)
@@ -266,7 +266,7 @@ The `pu` branch will be updated even if it is does not fast-forward,
 because it is prefixed with a plus sign; `tmp` will not be.
 
 * Peek at a remote's branch, without configuring the remote in your local
-repository:
+  repository:
 +
 ------------------------------------------------
 $ git fetch git://git.kernel.org/pub/scm/git/git.git maint
index 901faef1bfdcea991fe1b0a32ab5ecd22771967f..774cecc7ede787d22da5b656fe5299e4830d1d2e 100644 (file)
@@ -128,13 +128,18 @@ objecttype::
 
 objectsize::
        The size of the object (the same as 'git cat-file -s' reports).
-
+       Append `:disk` to get the size, in bytes, that the object takes up on
+       disk. See the note about on-disk sizes in the `CAVEATS` section below.
 objectname::
        The object name (aka SHA-1).
        For a non-ambiguous abbreviation of the object name append `:short`.
        For an abbreviation of the object name with desired length append
        `:short=<length>`, where the minimum length is MINIMUM_ABBREV. The
        length may be exceeded to ensure unique object names.
+deltabase::
+       This expands to the object name of the delta base for the
+       given object, if it is stored as a delta.  Otherwise it
+       expands to the null object name (all zeroes).
 
 upstream::
        The name of a local ref which can be considered ``upstream''
@@ -361,6 +366,20 @@ This prints the authorname, if present.
 git for-each-ref --format="%(refname)%(if)%(authorname)%(then) Authored by: %(authorname)%(end)"
 ------------
 
+CAVEATS
+-------
+
+Note that the sizes of objects on disk are reported accurately, but care
+should be taken in drawing conclusions about which refs or objects are
+responsible for disk usage. The size of a packed non-delta object may be
+much larger than the size of objects which delta against it, but the
+choice of which object is the base and which is the delta is arbitrary
+and is subject to change during a repack.
+
+Note also that multiple copies of an object may be present in the object
+database; in this case, it is undefined which copy's size or delta base
+will be reported.
+
 SEE ALSO
 --------
 linkgit:git-show-ref[1]
index 27304428a12ce9d0ee46e10e3c63069b4677838a..1af85d404f5191c18a8c587df06e7c0620066236 100644 (file)
@@ -504,9 +504,9 @@ Toggle it to make sure it is set to `false`. Also, search for
 "mailnews.wraplength" and set the value to 0.
 
 3. Disable the use of format=flowed:
-Edit..Preferences..Advanced..Config Editor.  Search for
-"mailnews.send_plaintext_flowed".
-Toggle it to make sure it is set to `false`.
+   Edit..Preferences..Advanced..Config Editor.  Search for
+   "mailnews.send_plaintext_flowed".
+   Toggle it to make sure it is set to `false`.
 
 After that is done, you should be able to compose email as you
 otherwise would (cut + paste, 'git format-patch' | 'git imap-send', etc),
@@ -629,14 +629,14 @@ EXAMPLES
 --------
 
 * Extract commits between revisions R1 and R2, and apply them on top of
-the current branch using 'git am' to cherry-pick them:
+  the current branch using 'git am' to cherry-pick them:
 +
 ------------
 $ git format-patch -k --stdout R1..R2 | git am -3 -k
 ------------
 
 * Extract all commits which are in the current branch but not in the
-origin branch:
+  origin branch:
 +
 ------------
 $ git format-patch origin
@@ -645,7 +645,7 @@ $ git format-patch origin
 For each commit a separate file is created in the current directory.
 
 * Extract all commits that lead to 'origin' since the inception of the
-project:
+  project:
 +
 ------------
 $ git format-patch --root origin
@@ -664,7 +664,7 @@ Note that non-Git "patch" programs won't understand renaming patches, so
 use it only when you know the recipient uses Git to apply your patch.
 
 * Extract three topmost commits from the current branch and format them
-as e-mailable patches:
+  as e-mailable patches:
 +
 ------------
 $ git format-patch -3
index c20ee6c7892518de4ba915e473e7ed736f68fe6b..a7442499f6d3985fa2ca57fa827d454b11479705 100644 (file)
@@ -137,7 +137,7 @@ The optional configuration variable `gc.packRefs` determines if
 it within all non-bare repos or it can be set to a boolean value.
 This defaults to true.
 
-The optional configuration variable `gc.commitGraph` determines if
+The optional configuration variable `gc.writeCommitGraph` determines if
 'git gc' should run 'git commit-graph write'. This can be set to a
 boolean value. This defaults to false.
 
index aab5453bbbb239931ee2006095135c8db9f0f9ea..c318bf87e174f18798a2fcb0530bec05a34c4c92 100644 (file)
@@ -118,9 +118,9 @@ format is chosen. The following values are currently supported:
 
 * "man": use the 'man' program as usual,
 * "woman": use 'emacsclient' to launch the "woman" mode in emacs
-(this only works starting with emacsclient versions 22),
+  (this only works starting with emacsclient versions 22),
 * "konqueror": use 'kfmclient' to open the man page in a new konqueror
-tab (see 'Note about konqueror' below).
+  tab (see 'Note about konqueror' below).
 
 Values for other tools can be used if there is a corresponding
 `man.<tool>.cmd` configuration entry (see below).
index 3c5a67fb9671f8c65e0830222c63bb5035a05c30..32880aafb0c55ce29f35490fd734587c38095066 100644 (file)
@@ -38,8 +38,6 @@ the repository to another place if --separate-git-dir is given).
 OPTIONS
 -------
 
---
-
 -q::
 --quiet::
 
@@ -111,8 +109,6 @@ into it.
 If you provide a 'directory', the command is run inside it. If this directory
 does not exist, it will be created.
 
---
-
 TEMPLATE DIRECTORY
 ------------------
 
@@ -132,7 +128,7 @@ The template directory will be one of the following (in order):
 The default template directory includes some directory structure, suggested
 "exclude patterns" (see linkgit:gitignore[5]), and sample hook files.
 
-The sample hooks are all disabled by default, To enable one of the
+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.
index e8ecdbf927ba5e7af79d3852a9a939c10052e45f..a54fe4401bd1a21c6bd133745ab8864882ca73d4 100644 (file)
@@ -29,7 +29,8 @@ OPTIONS
        The HTTP daemon command-line that will be executed.
        Command-line options may be specified here, and the
        configuration file will be added at the end of the command-line.
-       Currently apache2, lighttpd, mongoose, plackup and webrick are supported.
+       Currently apache2, lighttpd, mongoose, plackup, python and
+       webrick are supported.
        (Default: lighttpd)
 
 -m::
index f0a0280954f18532a1f12d4e5e6cae094c778784..3494a1db3ebf4991ed05011a8f4035807db7a9f6 100644 (file)
@@ -71,12 +71,12 @@ $ git p4 clone //depot/path/project
 ------------
 This:
 
-1.   Creates an empty Git repository in a subdirectory called 'project'.
+1. Creates an empty Git repository in a subdirectory called 'project'.
 +
-2.   Imports the full contents of the head revision from the given p4
-depot path into a single commit in the Git branch 'refs/remotes/p4/master'.
+2. Imports the full contents of the head revision from the given p4
+   depot path into a single commit in the Git branch 'refs/remotes/p4/master'.
 +
-3.   Creates a local branch, 'master' from this remote and checks it out.
+3. Creates a local branch, 'master' from this remote and checks it out.
 
 To reproduce the entire p4 history in Git, use the '@all' modifier on
 the depot path:
index a5fc54aeabccbeac32c73a610dbee45bfb5b0746..6a8a0d958bc63db19bfa4786e85e9e14061f1661 100644 (file)
@@ -73,6 +73,26 @@ be omitted--such a push will update a ref that `<src>` normally updates
 without any `<refspec>` on the command line.  Otherwise, missing
 `:<dst>` means to update the same ref as the `<src>`.
 +
+If <dst> doesn't start with `refs/` (e.g. `refs/heads/master`) we will
+try to infer where in `refs/*` on the destination <repository> it
+belongs based on the the type of <src> being pushed and whether <dst>
+is ambiguous.
++
+--
+* If <dst> unambiguously refers to a ref on the <repository> remote,
+  then push to that ref.
+
+* If <src> resolves to a ref starting with refs/heads/ or refs/tags/,
+  then prepend that to <dst>.
+
+* Other ambiguity resolutions might be added in the future, but for
+  now any other cases will error out with an error indicating what we
+  tried, and depending on the `advice.pushUnqualifiedRefname`
+  configuration (see linkgit:git-config[1]) suggest what refs/
+  namespace you may have wanted to push to.
+
+--
++
 The object referenced by <src> is used to update the <dst> reference
 on the remote side. Whether this is allowed depends on where in
 `refs/*` the <dst> reference lives as described in detail below, in
@@ -591,6 +611,9 @@ the ones in the examples below) can be configured as the default for
        `refs/remotes/satellite/master`) in the `mothership` repository;
        do the same for `dev` and `satellite/dev`.
 +
+See the section describing `<refspec>...` above for a discussion of
+the matching semantics.
++
 This is to emulate `git fetch` run on the `mothership` using `git
 push` that is run in the opposite direction in order to integrate
 the work done on `satellite`, and is often necessary when you can
index 8cf952b4de669ea3e7275c5d10087005548574eb..70562dc4c0235d53501bab56ff98af6169b8f968 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git quiltimport' [--dry-run | -n] [--author <author>] [--patches <dir>]
-               [--series <file>]
+               [--series <file>] [--keep-non-patch]
 
 
 DESCRIPTION
@@ -56,6 +56,9 @@ The default for the series file is <patches>/series
 or the value of the `$QUILT_SERIES` environment
 variable.
 
+--keep-non-patch::
+       Pass `-b` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index dff17b31788fec085b91314b3b3328c689050628..7e695b30e455245d8c187b7be0402e35bb83362c 100644 (file)
@@ -462,6 +462,12 @@ without an explicit `--interactive`.
 +
 See also INCOMPATIBLE OPTIONS below.
 
+-y <cmd>::
+       This is the same as passing `--reschedule-failed-exec` before
+       `-x <cmd>`, i.e. it appends the specified `exec` command and
+       turns on the mode where failed `exec` commands are automatically
+       rescheduled.
+
 --root::
        Rebase all commits reachable from <branch>, instead of
        limiting them with an <upstream>.  This allows you to rebase
@@ -501,18 +507,15 @@ See also INCOMPATIBLE OPTIONS below.
        with care: the final stash application after a successful
        rebase might result in non-trivial conflicts.
 
+--reschedule-failed-exec::
+--no-reschedule-failed-exec::
+       Automatically reschedule `exec` commands that failed. This only makes
+       sense in interactive mode (or when an `--exec` option was provided).
+
 INCOMPATIBLE OPTIONS
 --------------------
 
-git-rebase has many flags that are incompatible with each other,
-predominantly due to the fact that it has three different underlying
-implementations:
-
- * one based on linkgit:git-am[1] (the default)
- * one based on git-merge-recursive (merge backend)
- * one based on linkgit:git-cherry-pick[1] (interactive backend)
-
-Flags only understood by the am backend:
+The following options:
 
  * --committer-date-is-author-date
  * --ignore-date
@@ -520,15 +523,12 @@ Flags only understood by the am backend:
  * --ignore-whitespace
  * -C
 
-Flags understood by both merge and interactive backends:
+are incompatible with the following options:
 
  * --merge
  * --strategy
  * --strategy-option
  * --allow-empty-message
-
-Flags only understood by the interactive backend:
-
  * --[no-]autosquash
  * --rebase-merges
  * --preserve-merges
@@ -539,7 +539,7 @@ Flags only understood by the interactive backend:
  * --edit-todo
  * --root when used in combination with --onto
 
-Other incompatible flag pairs:
+In addition, the following pairs of options are incompatible:
 
  * --preserve-merges and --interactive
  * --preserve-merges and --signoff
@@ -570,8 +570,9 @@ it to keep commits that started empty.
 Directory rename detection
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The merge and interactive backends work fine with
-directory rename detection.  The am backend sometimes does not.
+Directory rename heuristics are enabled in the merge and interactive
+backends.  Due to the lack of accurate tree information, directory
+rename detection is disabled in the am backend.
 
 include::merge-strategies.txt[]
 
@@ -979,7 +980,7 @@ when the merge operation did not even start), it is rescheduled immediately.
 
 At this time, the `merge` command will *always* use the `recursive`
 merge strategy for regular merges, and `octopus` for octopus merges,
-strategy, with no way to choose a different one. To work around
+with no way to choose a different one. To work around
 this, an `exec` command can be used to call `git merge` explicitly,
 using the fact that the labels are worktree-local refs (the ref
 `refs/rewritten/onto` would correspond to the label `onto`, for example).
index 9f69ae8b693a15184f26f65a44e552fb31a0bd32..132f8e55f67b2e2a40f0058888f4f8260c51f270 100644 (file)
@@ -115,17 +115,17 @@ $ git pull git://info.example.com/ nitfol  <4>
 ------------
 +
 <1> You are happily working on something, and find the changes
-in these files are in good order.  You do not want to see them
-when you run `git diff`, because you plan to work on other files
-and changes with these files are distracting.
+    in these files are in good order.  You do not want to see them
+    when you run `git diff`, because you plan to work on other files
+    and changes with these files are distracting.
 <2> Somebody asks you to pull, and the changes sound worthy of merging.
 <3> However, you already dirtied the index (i.e. your index does
-not match the `HEAD` commit).  But you know the pull you are going
-to make does not affect `frotz.c` or `filfre.c`, so you revert the
-index changes for these two files.  Your changes in working tree
-remain there.
+    not match the `HEAD` commit).  But you know the pull you are going
+    to make does not affect `frotz.c` or `filfre.c`, so you revert the
+    index changes for these two files.  Your changes in working tree
+    remain there.
 <4> Then you can pull and merge, leaving `frotz.c` and `filfre.c`
-changes still in the working tree.
+    changes still in the working tree.
 
 Undo a commit and redo::
 +
@@ -137,12 +137,12 @@ $ git commit -a -c ORIG_HEAD  <3>
 ------------
 +
 <1> This is most often done when you remembered what you
-just committed is incomplete, or you misspelled your commit
-message, or both.  Leaves working tree as it was before "reset".
+    just committed is incomplete, or you misspelled your commit
+    message, or both.  Leaves working tree as it was before "reset".
 <2> Make corrections to working tree files.
 <3> "reset" copies the old head to `.git/ORIG_HEAD`; redo the
-commit by starting with its log message.  If you do not need to
-edit the message further, you can give `-C` option instead.
+    commit by starting with its log message.  If you do not need to
+    edit the message further, you can give `-C` option instead.
 +
 See also the `--amend` option to linkgit:git-commit[1].
 
@@ -155,9 +155,9 @@ $ git checkout topic/wip   <3>
 ------------
 +
 <1> You have made some commits, but realize they were premature
-to be in the `master` branch.  You want to continue polishing
-them in a topic branch, so create `topic/wip` branch off of the
-current `HEAD`.
+    to be in the `master` branch.  You want to continue polishing
+    them in a topic branch, so create `topic/wip` branch off of the
+    current `HEAD`.
 <2> Rewind the master branch to get rid of those three commits.
 <3> Switch to `topic/wip` branch and keep working.
 
@@ -169,10 +169,10 @@ $ git reset --hard HEAD~3   <1>
 ------------
 +
 <1> The last three commits (`HEAD`, `HEAD^`, and `HEAD~2`) were bad
-and you do not want to ever see them again.  Do *not* do this if
-you have already given these commits to somebody else.  (See the
-"RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1] for
-the implications of doing so.)
+    and you do not want to ever see them again.  Do *not* do this if
+    you have already given these commits to somebody else.  (See the
+    "RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1]
+    for the implications of doing so.)
 
 Undo a merge or pull::
 +
@@ -189,18 +189,18 @@ $ git reset --hard ORIG_HEAD       <4>
 ------------
 +
 <1> Try to update from the upstream resulted in a lot of
-conflicts; you were not ready to spend a lot of time merging
-right now, so you decide to do that later.
+    conflicts; you were not ready to spend a lot of time merging
+    right now, so you decide to do that later.
 <2> "pull" has not made merge commit, so `git reset --hard`
-which is a synonym for `git reset --hard HEAD` clears the mess
-from the index file and the working tree.
+    which is a synonym for `git reset --hard HEAD` clears the mess
+    from the index file and the working tree.
 <3> Merge a topic branch into the current branch, which resulted
-in a fast-forward.
+    in a fast-forward.
 <4> But you decided that the topic branch is not ready for public
-consumption yet.  "pull" or "merge" always leaves the original
-tip of the current branch in `ORIG_HEAD`, so resetting hard to it
-brings your index file and the working tree back to that state,
-and resets the tip of the branch to that commit.
+    consumption yet.  "pull" or "merge" always leaves the original
+    tip of the current branch in `ORIG_HEAD`, so resetting hard to it
+    brings your index file and the working tree back to that state,
+    and resets the tip of the branch to that commit.
 
 Undo a merge or pull inside a dirty working tree::
 +
@@ -214,14 +214,14 @@ $ git reset --merge ORIG_HEAD      <2>
 ------------
 +
 <1> Even if you may have local modifications in your
-working tree, you can safely say `git pull` when you know
-that the change in the other branch does not overlap with
-them.
+    working tree, you can safely say `git pull` when you know
+    that the change in the other branch does not overlap with
+    them.
 <2> After inspecting the result of the merge, you may find
-that the change in the other branch is unsatisfactory.  Running
-`git reset --hard ORIG_HEAD` will let you go back to where you
-were, but it will discard your local changes, which you do not
-want.  `git reset --merge` keeps your local changes.
+    that the change in the other branch is unsatisfactory.  Running
+    `git reset --hard ORIG_HEAD` will let you go back to where you
+    were, but it will discard your local changes, which you do not
+    want.  `git reset --merge` keeps your local changes.
 
 
 Interrupted workflow::
index 62c6c76f2736e0ee2dd61d3ac25a4962a9db0fc7..1afe9fc858ea7dcd05ae5f77c994af8f17f5bf52 100644 (file)
@@ -33,7 +33,7 @@ This is what linkgit:git-format-patch[1] generates.  Most headers and MIME
 formatting are ignored.
 
 2. The original format used by Greg Kroah-Hartman's 'send_lots_of_email.pl'
-script
+   script
 +
 This format expects the first line of the file to contain the "Cc:" value
 and the "Subject:" of the message as the second line.
index d28e6154c6863d2d87c52251669e305caf7f995a..ab4d271925da7983b16867d89e898bbed17ae469 100644 (file)
@@ -37,8 +37,8 @@ OPTIONS
 
        Show the HEAD reference, even if it would normally be filtered out.
 
---tags::
 --heads::
+--tags::
 
        Limit to "refs/heads" and "refs/tags", respectively.  These options
        are not mutually exclusive; when given both, references stored in
index d9f422d5600af6b112eba692d5c5607cdb4169ef..861d821d7f26ec88818008f4c6825bfcad2590ce 100644 (file)
@@ -197,31 +197,33 @@ codes can be interpreted as follows:
 Ignored files are not listed, unless `--ignored` option is in effect,
 in which case `XY` are `!!`.
 
-    X          Y     Meaning
-    -------------------------------------------------
-            [AMD]   not updated
-    M        [ MD]   updated in index
-    A        [ MD]   added to index
-    D                deleted from index
-    R        [ MD]   renamed in index
-    C        [ MD]   copied in index
-    [MARC]           index and work tree matches
-    [ MARC]     M    work tree changed since index
-    [ MARC]     D    deleted in work tree
-    [ D]        R    renamed in work tree
-    [ D]        C    copied in work tree
-    -------------------------------------------------
-    D           D    unmerged, both deleted
-    A           U    unmerged, added by us
-    U           D    unmerged, deleted by them
-    U           A    unmerged, added by them
-    D           U    unmerged, deleted by us
-    A           A    unmerged, both added
-    U           U    unmerged, both modified
-    -------------------------------------------------
-    ?           ?    untracked
-    !           !    ignored
-    -------------------------------------------------
+....
+X          Y     Meaning
+-------------------------------------------------
+        [AMD]   not updated
+M        [ MD]   updated in index
+A        [ MD]   added to index
+D                deleted from index
+R        [ MD]   renamed in index
+C        [ MD]   copied in index
+[MARC]           index and work tree matches
+[ MARC]     M    work tree changed since index
+[ MARC]     D    deleted in work tree
+[ D]        R    renamed in work tree
+[ D]        C    copied in work tree
+-------------------------------------------------
+D           D    unmerged, both deleted
+A           U    unmerged, added by us
+U           D    unmerged, deleted by them
+U           A    unmerged, added by them
+D           U    unmerged, deleted by us
+A           A    unmerged, both added
+U           U    unmerged, both modified
+-------------------------------------------------
+?           ?    untracked
+!           !    ignored
+-------------------------------------------------
+....
 
 Submodules have more state and instead report
                M    the submodule has a different HEAD than
@@ -281,14 +283,16 @@ don't recognize.
 If `--branch` is given, a series of header lines are printed with
 information about the current branch.
 
-    Line                                     Notes
-    ------------------------------------------------------------
-    # branch.oid <commit> | (initial)        Current commit.
-    # branch.head <branch> | (detached)      Current branch.
-    # branch.upstream <upstream_branch>      If upstream is set.
-    # branch.ab +<ahead> -<behind>           If upstream is set and
-                                            the commit is present.
-    ------------------------------------------------------------
+....
+Line                                     Notes
+------------------------------------------------------------
+# branch.oid <commit> | (initial)        Current commit.
+# branch.head <branch> | (detached)      Current branch.
+# branch.upstream <upstream_branch>      If upstream is set.
+# branch.ab +<ahead> -<behind>           If upstream is set and
+                                        the commit is present.
+------------------------------------------------------------
+....
 
 ### Changed Tracked Entries
 
@@ -306,56 +310,60 @@ Renamed or copied entries have the following format:
 
     2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><score> <path><sep><origPath>
 
-    Field       Meaning
-    --------------------------------------------------------
-    <XY>        A 2 character field containing the staged and
-               unstaged XY values described in the short format,
-               with unchanged indicated by a "." rather than
-               a space.
-    <sub>       A 4 character field describing the submodule state.
-               "N..." when the entry is not a submodule.
-               "S<c><m><u>" when the entry is a submodule.
-               <c> is "C" if the commit changed; otherwise ".".
-               <m> is "M" if it has tracked changes; otherwise ".".
-               <u> is "U" if there are untracked changes; otherwise ".".
-    <mH>        The octal file mode in HEAD.
-    <mI>        The octal file mode in the index.
-    <mW>        The octal file mode in the worktree.
-    <hH>        The object name in HEAD.
-    <hI>        The object name in the index.
-    <X><score>  The rename or copy score (denoting the percentage
-               of similarity between the source and target of the
-               move or copy). For example "R100" or "C75".
-    <path>      The pathname.  In a renamed/copied entry, this
-               is the target path.
-    <sep>       When the `-z` option is used, the 2 pathnames are separated
-               with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
-               byte separates them.
-    <origPath>  The pathname in the commit at HEAD or in the index.
-               This is only present in a renamed/copied entry, and
-               tells where the renamed/copied contents came from.
-    --------------------------------------------------------
+....
+Field       Meaning
+--------------------------------------------------------
+<XY>        A 2 character field containing the staged and
+           unstaged XY values described in the short format,
+           with unchanged indicated by a "." rather than
+           a space.
+<sub>       A 4 character field describing the submodule state.
+           "N..." when the entry is not a submodule.
+           "S<c><m><u>" when the entry is a submodule.
+           <c> is "C" if the commit changed; otherwise ".".
+           <m> is "M" if it has tracked changes; otherwise ".".
+           <u> is "U" if there are untracked changes; otherwise ".".
+<mH>        The octal file mode in HEAD.
+<mI>        The octal file mode in the index.
+<mW>        The octal file mode in the worktree.
+<hH>        The object name in HEAD.
+<hI>        The object name in the index.
+<X><score>  The rename or copy score (denoting the percentage
+           of similarity between the source and target of the
+           move or copy). For example "R100" or "C75".
+<path>      The pathname.  In a renamed/copied entry, this
+           is the target path.
+<sep>       When the `-z` option is used, the 2 pathnames are separated
+           with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
+           byte separates them.
+<origPath>  The pathname in the commit at HEAD or in the index.
+           This is only present in a renamed/copied entry, and
+           tells where the renamed/copied contents came from.
+--------------------------------------------------------
+....
 
 Unmerged entries have the following format; the first character is
 a "u" to distinguish from ordinary changed entries.
 
     u <xy> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>
 
-    Field       Meaning
-    --------------------------------------------------------
-    <XY>        A 2 character field describing the conflict type
-               as described in the short format.
-    <sub>       A 4 character field describing the submodule state
-               as described above.
-    <m1>        The octal file mode in stage 1.
-    <m2>        The octal file mode in stage 2.
-    <m3>        The octal file mode in stage 3.
-    <mW>        The octal file mode in the worktree.
-    <h1>        The object name in stage 1.
-    <h2>        The object name in stage 2.
-    <h3>        The object name in stage 3.
-    <path>      The pathname.
-    --------------------------------------------------------
+....
+Field       Meaning
+--------------------------------------------------------
+<XY>        A 2 character field describing the conflict type
+           as described in the short format.
+<sub>       A 4 character field describing the submodule state
+           as described above.
+<m1>        The octal file mode in stage 1.
+<m2>        The octal file mode in stage 2.
+<m3>        The octal file mode in stage 3.
+<mW>        The octal file mode in the worktree.
+<h1>        The object name in stage 1.
+<h2>        The object name in stage 2.
+<h3>        The object name in stage 3.
+<path>      The pathname.
+--------------------------------------------------------
+....
 
 ### Other Items
 
index f2d644e3af1849d842567bfb7a2c38083984545d..a74e7b926d030f6f385e49be68714133f6e3c06e 100644 (file)
@@ -237,16 +237,16 @@ your repository directly), then others will have already seen
 the old tag. In that case you can do one of two things:
 
 . The sane thing.
-Just admit you screwed up, and use a different name. Others have
-already seen one tag-name, and if you keep the same name, you
-may be in the situation that two people both have "version X",
-but they actually have 'different' "X"'s.  So just call it "X.1"
-and be done with it.
+  Just admit you screwed up, and use a different name. Others have
+  already seen one tag-name, and if you keep the same name, you
+  may be in the situation that two people both have "version X",
+  but they actually have 'different' "X"'s.  So just call it "X.1"
+  and be done with it.
 
 . The insane thing.
-You really want to call the new version "X" too, 'even though'
-others have already seen the old one. So just use 'git tag -f'
-again, as if you hadn't already published the old one.
+  You really want to call the new version "X" too, 'even though'
+  others have already seen the old one. So just use 'git tag -f'
+  again, as if you hadn't already published the old one.
 
 However, Git does *not* (and it should not) change tags behind
 users back. So if somebody already got the old tag, doing a
index 998f52d3df71e29d4178251de375d70cd570a1d8..9822c1eb1add168cdb30426bab04d925504d0205 100644 (file)
@@ -22,7 +22,6 @@ The UI for the protocol is on the 'git fetch-pack' side, and the
 program pair is meant to be used to pull updates from a remote
 repository.  For push operations, see 'git send-pack'.
 
-
 OPTIONS
 -------
 
index b8392fc3300cfa7d7c37e37dd069246f20ceadb1..a2310fb920ecf64cacf6617c97383bbc4443a03c 100644 (file)
@@ -344,7 +344,9 @@ automatic line ending conversion based on your platform.
 
 Use the following attributes if your '*.ps1' files are UTF-16 little
 endian encoded without BOM and you want Git to use Windows line endings
-in the working directory. Please note, it is highly recommended to
+in the working directory (use `UTF-16-LE-BOM` instead of `UTF-16LE` if
+you want UTF-16 little endian with BOM).
+Please note, it is highly recommended to
 explicitly define the line endings with `eol` if the `working-tree-encoding`
 attribute is used to avoid ambiguity.
 
index c0a60f315811c788f3431b5757c38ffcea487386..c970d9fe438a091dc19900a4de1973ac4bcc4434 100644 (file)
@@ -242,7 +242,8 @@ textual diff has an added or a deleted line that matches the given
 regular expression.  This means that it will detect in-file (or what
 rename-detection considers the same file) moves, which is noise.  The
 implementation runs diff twice and greps, and this can be quite
-expensive.
+expensive.  To speed things up binary files without textconv filters
+will be ignored.
 
 When `-S` or `-G` are used without `--pickaxe-all`, only filepairs
 that match their respective criterion are kept in the output.  When
index c0a326e3883c321f6a5b0c545d9aee9cdd1a5d8a..92535dbac53ac49d89ef6d8ce62dd2920c83c445 100644 (file)
@@ -207,8 +207,8 @@ subsection on linkgit:gitweb[1] manpage.
 
 $strict_export::
        Only allow viewing of repositories also shown on the overview page.
-       This for example makes `$gitweb_export_ok` file decide if repository is
-       available and not only if it is shown.  If `$gitweb_list` points to
+       This for example makes `$export_ok` file decide if repository is
+       available and not only if it is shown.  If `$projects_list` points to
        file with list of project, only those repositories listed would be
        available for gitweb.  Can be set during building gitweb via
        `GITWEB_STRICT_EXPORT`.  By default this variable is not set, which
@@ -684,7 +684,7 @@ compressed tar archive) and "zip"; please consult gitweb sources for
 a definitive list.  By default only "tgz" is offered.
 +
 This feature can be configured on a per-repository basis via
-repository's `gitweb.blame` configuration variable, which contains
+repository's `gitweb.snapshot` configuration variable, which contains
 a comma separated list of formats or "none" to disable snapshots.
 Unknown values are ignored.
 
index 0d2aa48c63856aba78d559445186a9b9e6bddd7d..023ca95e7c39d88fcac832d73a7454e9fb71e5a1 100644 (file)
@@ -404,6 +404,8 @@ these forms:
 - "`!ATTR`" requires that the attribute `ATTR` be
   unspecified.
 +
+Note that when matching against a tree object, attributes are still
+obtained from working tree, not from the given tree object.
 
 exclude;;
        After a path matches any non-exclude pathspec, it will be run
index 417b638cd803e6cf3b106f3bd7333ba4f72d800a..de6953108cbc5324b9f6e9dacf23482aa698f45a 100644 (file)
@@ -134,6 +134,8 @@ The placeholders are:
 - '%cI': committer date, strict ISO 8601 format
 - '%d': ref names, like the --decorate option of linkgit:git-log[1]
 - '%D': ref names without the " (", ")" wrapping.
+- '%S': ref name given on the command line by which the commit was reached
+  (like `git log --source`), only works with `git log`
 - '%e': encoding
 - '%s': subject
 - '%f': sanitized subject line, suitable for a filename
index bab5f50b1724913c7607180897b7f92d1517dda9..8a4867998e0bf2c76953ca483773971b005a1479 100644 (file)
@@ -13,8 +13,6 @@ has a line that matches `<pattern>`), unless otherwise noted.
 Note that these are applied before commit
 ordering and formatting options, such as `--reverse`.
 
---
-
 -<number>::
 -n <number>::
 --max-count=<number>::
@@ -272,13 +270,13 @@ depending on a few rules:
 +
 --
 1. If the starting point is specified as `ref@{Nth}`, show the index
-format.
+   format.
 +
 2. If the starting point was specified as `ref@{now}`, show the
-timestamp format.
+   timestamp format.
 +
 3. If neither was used, but `--date` was given on the command line, show
-the timestamp in the format requested by `--date`.
+   the timestamp in the format requested by `--date`.
 +
 4. Otherwise, show the index format.
 --
@@ -308,8 +306,6 @@ ifdef::git-rev-list[]
        `<header>` text will be printed with each progress update.
 endif::git-rev-list[]
 
---
-
 History Simplification
 ~~~~~~~~~~~~~~~~~~~~~~
 
@@ -734,8 +730,13 @@ specification contained in <path>.
 +
 The form '--filter=tree:<depth>' omits all blobs and trees whose depth
 from the root tree is >= <depth> (minimum depth if an object is located
-at multiple depths in the commits traversed). Currently, only <depth>=0
-is supported, which omits all blobs and trees.
+at multiple depths in the commits traversed). <depth>=0 will not include
+any trees or blobs unless included explicitly in the command-line (or
+standard input when --stdin is used). <depth>=1 will include only the
+tree and blobs which are referenced directly by a commit reachable from
+<commit> or an explicitly-given object. <depth>=2 is like <depth>=1
+while also including trees and blobs one more level removed from an
+explicitly-given commit or tree.
 
 --no-filter::
        Turn off any previous `--filter=` argument.
index 9febfb1d528b2764d6d603ea25eae0ac6b048f68..c97428c2c3495e3f575ed944027740e882128482 100644 (file)
@@ -48,6 +48,11 @@ Functions
        is not sorted, this function has the side effect of sorting
        it.
 
+`oid_array_filter`::
+       Apply the callback function `want` to each entry in the array,
+       retaining only the entries for which the function returns true.
+       Preserve the order of the entries that are retained.
+
 Examples
 --------
 
index cc0474ba3ee4b04153752340328233437d5c7b05..16452a0504c8fa5b9b1b62cb907b96315db43942 100644 (file)
@@ -76,7 +76,7 @@ CHUNK DATA:
       of the ith commit. Stores value 0x7000000 if no parent in that
       position. If there are more than two parents, the second value
       has its most-significant bit on and the other bits store an array
-      position into the Large Edge List chunk.
+      position into the Extra Edge List chunk.
     * The next 8 bytes store the generation number of the commit and
       the commit time in seconds since EPOCH. The generation number
       uses the higher 30 bits of the first 4 bytes, while the commit
@@ -84,7 +84,7 @@ CHUNK DATA:
       2 bits of the lowest byte, storing the 33rd and 34th bit of the
       commit time.
 
-  Large Edge List (ID: {'E', 'D', 'G', 'E'}) [Optional]
+  Extra Edge List (ID: {'E', 'D', 'G', 'E'}) [Optional]
       This list of 4-byte values store the second through nth parents for
       all octopus merges. The second parent value in the commit data stores
       an array position within this list along with the most-significant bit
index 6ac774d5f665614848cdaba9e4c97d86a2453913..7a2375a55d074514c5ebd851fe55ff27541c207c 100644 (file)
@@ -22,6 +22,16 @@ protocol-common.txt. When the grammar indicate `PKT-LINE(...)`, unless
 otherwise noted the usual pkt-line LF rules apply: the sender SHOULD
 include a LF, but the receiver MUST NOT complain if it is not present.
 
+An error packet is a special pkt-line that contains an error string.
+
+----
+  error-line     =  PKT-LINE("ERR" SP explanation-text)
+----
+
+Throughout the protocol, where `PKT-LINE(...)` is expected, an error packet MAY
+be sent. Once this packet is sent by a client or a server, the data transfer
+process defined in this protocol is terminated.
+
 Transports
 ----------
 There are three transports over which the packfile protocol is
@@ -89,13 +99,6 @@ process on the server side over the Git protocol is this:
      "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" |
      nc -v example.com 9418
 
-If the server refuses the request for some reasons, it could abort
-gracefully with an error message.
-
-----
-  error-line     =  PKT-LINE("ERR" SP explanation-text)
-----
-
 
 SSH Transport
 -------------
@@ -398,12 +401,11 @@ from the client).
 Then the server will start sending its packfile data.
 
 ----
-  server-response = *ack_multi ack / nak / error-line
+  server-response = *ack_multi ack / nak
   ack_multi       = PKT-LINE("ACK" SP obj-id ack_status)
   ack_status      = "continue" / "common" / "ready"
   ack             = PKT-LINE("ACK" SP obj-id)
   nak             = PKT-LINE("NAK")
-  error-line     =  PKT-LINE("ERR" SP explanation-text)
 ----
 
 A simple clone may look like this (with no 'have' lines):
index 1ef66bd788a0997f73ec736c806a5aadad63ee39..896c7b3878869de8d3564b42abcef9d4ea5dedec 100644 (file)
@@ -114,7 +114,7 @@ their "<name>.pack" and "<name>.idx" files.
   that it has, either because the local repository has that object in one of
   its promisor packfiles, or because another promisor object refers to it.
 +
-When Git encounters a missing object, Git can see if it a promisor object
+When Git encounters a missing object, Git can see if it is a promisor object
 and handle it appropriately.  If not, Git can report a corruption.
 +
 This means that there is no need for the client to explicitly maintain an
index 09e4e0273fd515254b41cf2d36e3b6083de1d4ee..ead85ce35cf2342335af69221e4b4e956b9f66fd 100644 (file)
@@ -296,7 +296,13 @@ included in the client's request:
        Request that various objects from the packfile be omitted
        using one of several filtering techniques. These are intended
        for use with partial clone and partial fetch operations. See
-       `rev-list` for possible "filter-spec" values.
+       `rev-list` for possible "filter-spec" values. When communicating
+       with other processes, senders SHOULD translate scaled integers
+       (e.g. "1k") into a fully-expanded form (e.g. "1024") to aid
+       interoperability with older receivers that may not understand
+       newly-invented scaling suffixes. However, receivers SHOULD
+       accept the following suffixes: 'k', 'm', and 'g' for 1024,
+       1048576, and 1073741824, respectively.
 
 If the 'ref-in-want' feature is advertised, the following argument can
 be included in the client's request as well as the potential addition of
@@ -307,6 +313,16 @@ the 'wanted-refs' section in the server's response as explained below.
        particular ref, where <ref> is the full name of a ref on the
        server.
 
+If the 'sideband-all' feature is advertised, the following argument can be
+included in the client's request:
+
+    sideband-all
+       Instruct the server to send the whole response multiplexed, not just
+       the packfile section. All non-flush and non-delim PKT-LINE in the
+       response (not only in the packfile section) will then start with a byte
+       indicating its sideband (1, 2, or 3), and the server may send "0005\2"
+       (a PKT-LINE of sideband 2 with no payload) as a keepalive packet.
+
 The response of `fetch` is broken into a number of sections separated by
 delimiter packets (0001), with each section beginning with its section
 header.
index 1a44c811aa56330327172cf693c61f9a221e4e16..afa411d727532f95278438851a5ad09292fb7f48 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -186,6 +186,12 @@ all::
 # in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO
 # wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined.
 #
+# Define BLK_SHA256 to use the built-in SHA-256 routines.
+#
+# Define GCRYPT_SHA256 to use the SHA-256 routines in libgcrypt.
+#
+# Define OPENSSL_SHA256 to use the SHA-256 routines in OpenSSL.
+#
 # Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin).
 #
 # Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
@@ -628,7 +634,6 @@ SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-rebase--am
 SCRIPT_LIB += git-rebase--common
 SCRIPT_LIB += git-rebase--preserve-merges
-SCRIPT_LIB += git-rebase--merge
 SCRIPT_LIB += git-sh-setup
 SCRIPT_LIB += git-sh-i18n
 
@@ -684,6 +689,7 @@ SCRIPTS = $(SCRIPT_SH_INS) \
 
 ETAGS_TARGET = TAGS
 
+FUZZ_OBJS += fuzz-commit-graph.o
 FUZZ_OBJS += fuzz-pack-headers.o
 FUZZ_OBJS += fuzz-pack-idx.o
 
@@ -724,7 +730,9 @@ TEST_BUILTINS_OBJS += test-dump-split-index.o
 TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
 TEST_BUILTINS_OBJS += test-genrandom.o
+TEST_BUILTINS_OBJS += test-hash.o
 TEST_BUILTINS_OBJS += test-hashmap.o
+TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-index-version.o
 TEST_BUILTINS_OBJS += test-json-writer.o
 TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -747,6 +755,7 @@ TEST_BUILTINS_OBJS += test-run-command.o
 TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha1-array.o
+TEST_BUILTINS_OBJS += test-sha256.o
 TEST_BUILTINS_OBJS += test-sigchain.o
 TEST_BUILTINS_OBJS += test-strcmp-offset.o
 TEST_BUILTINS_OBJS += test-string-list.o
@@ -1646,6 +1655,19 @@ endif
 endif
 endif
 
+ifdef OPENSSL_SHA256
+       EXTLIBS += $(LIB_4_CRYPTO)
+       BASIC_CFLAGS += -DSHA256_OPENSSL
+else
+ifdef GCRYPT_SHA256
+       BASIC_CFLAGS += -DSHA256_GCRYPT
+       EXTLIBS += -lgcrypt
+else
+       LIB_OBJS += sha256/block/sha256.o
+       BASIC_CFLAGS += -DSHA256_BLK
+endif
+endif
+
 ifdef SHA1_MAX_BLOCK_SIZE
        LIB_OBJS += compat/sha1-chunked.o
        BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
@@ -3103,7 +3125,7 @@ cover_db_html: cover_db
 # An example command to build against libFuzzer from LLVM 4.0.0:
 #
 # make CC=clang CXX=clang++ \
-#      FUZZ_CXXFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
+#      CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
 #      LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
 #      fuzz-all
 #
index 5f35656409b1d51abf111efa5bbcc7f5d570aaf0..567209aa79afee0443dd0d7049343e1a004ae784 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -9,6 +9,7 @@ int advice_push_non_ff_matching = 1;
 int advice_push_already_exists = 1;
 int advice_push_fetch_first = 1;
 int advice_push_needs_force = 1;
+int advice_push_unqualified_ref_name = 1;
 int advice_status_hints = 1;
 int advice_status_u_option = 1;
 int advice_commit_before_merge = 1;
@@ -63,6 +64,7 @@ static struct {
        { "pushAlreadyExists", &advice_push_already_exists },
        { "pushFetchFirst", &advice_push_fetch_first },
        { "pushNeedsForce", &advice_push_needs_force },
+       { "pushUnqualifiedRefName", &advice_push_unqualified_ref_name },
        { "statusHints", &advice_status_hints },
        { "statusUoption", &advice_status_u_option },
        { "commitBeforeMerge", &advice_commit_before_merge },
index 696bf0e7d29ee107c5faf10a59985c0f49612495..f875f8cd8da5fdb6e570770ecc7ab4461745f827 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -9,6 +9,7 @@ extern int advice_push_non_ff_matching;
 extern int advice_push_already_exists;
 extern int advice_push_fetch_first;
 extern int advice_push_needs_force;
+extern int advice_push_unqualified_ref_name;
 extern int advice_status_hints;
 extern int advice_status_u_option;
 extern int advice_commit_before_merge;
diff --git a/alias.c b/alias.c
index a7e4e57130d46188a4a0ee93871375218041344c..c4715380205b5f8dd032b16ade846842fa3c23b9 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -47,8 +47,8 @@ void list_aliases(struct string_list *list)
 #define SPLIT_CMDLINE_BAD_ENDING 1
 #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
 static const char *split_cmdline_errors[] = {
-       "cmdline ends with \\",
-       "unclosed quote"
+       N_("cmdline ends with \\"),
+       N_("unclosed quote")
 };
 
 int split_cmdline(char *cmdline, const char ***argv)
diff --git a/alloc.c b/alloc.c
index e7aa81b7aa48c14f2a98894eb6e8c2e28998945e..1c64c4dd1629c63a8bb2fff6e580ee0c648a0730 100644 (file)
--- a/alloc.c
+++ b/alloc.c
@@ -99,18 +99,23 @@ void *alloc_object_node(struct repository *r)
        return obj;
 }
 
-unsigned int alloc_commit_index(struct repository *r)
+static unsigned int alloc_commit_index(struct repository *r)
 {
        return r->parsed_objects->commit_count++;
 }
 
-void *alloc_commit_node(struct repository *r)
+void init_commit_node(struct repository *r, struct commit *c)
 {
-       struct commit *c = alloc_node(r->parsed_objects->commit_state, sizeof(struct commit));
        c->object.type = OBJ_COMMIT;
        c->index = alloc_commit_index(r);
        c->graph_pos = COMMIT_NOT_FROM_GRAPH;
        c->generation = GENERATION_NUMBER_INFINITY;
+}
+
+void *alloc_commit_node(struct repository *r)
+{
+       struct commit *c = alloc_node(r->parsed_objects->commit_state, sizeof(struct commit));
+       init_commit_node(r, c);
        return c;
 }
 
diff --git a/alloc.h b/alloc.h
index ba356ed8478d13f05f49efd5bacccaac3e69dfd7..ed1071c11ea3a6d0f7d55da46df4acf524a32565 100644 (file)
--- a/alloc.h
+++ b/alloc.h
@@ -9,11 +9,11 @@ struct repository;
 
 void *alloc_blob_node(struct repository *r);
 void *alloc_tree_node(struct repository *r);
+void init_commit_node(struct repository *r, struct commit *c);
 void *alloc_commit_node(struct repository *r);
 void *alloc_tag_node(struct repository *r);
 void *alloc_object_node(struct repository *r);
 void alloc_report(struct repository *r);
-unsigned int alloc_commit_index(struct repository *r);
 
 struct alloc_state *allocate_alloc_state(void);
 void clear_alloc_state(struct alloc_state *s);
diff --git a/apply.c b/apply.c
index 01793d612620246b14fa57f6ce3ed6c33df65d0f..3703bfc8d03deb3be3a3fa9a45a2dd64b0a8eafd 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -3352,7 +3352,8 @@ static int checkout_target(struct index_state *istate,
 
        costate.refresh_cache = 1;
        costate.istate = istate;
-       if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
+       if (checkout_entry(ce, &costate, NULL, NULL) ||
+           lstat(ce->name, st))
                return error(_("cannot checkout %s"), ce->name);
        return 0;
 }
index a58e1a8ebf874afc856b03f2203ee32d79183b31..4aabd566fbb8af7065ebe8fa62afe8e8e70abe04 100644 (file)
@@ -142,7 +142,7 @@ static int stream_blocked(const struct object_id *oid)
  * string and appends it to a struct strbuf.
  */
 static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
-                                     const char *value, unsigned int valuelen)
+                                    const char *value, unsigned int valuelen)
 {
        int len, tmp;
 
index fd556c28e420732bbe625b68663f9099a5316f96..1f98324a930e39aa1a7c41e78b4fcd6450899c66 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -36,8 +36,8 @@ void init_archivers(void)
 }
 
 static void format_subst(const struct commit *commit,
-                         const char *src, size_t len,
-                         struct strbuf *buf)
+                        const char *src, size_t len,
+                        struct strbuf *buf)
 {
        char *to_free = NULL;
        struct strbuf fmt = STRBUF_INIT;
@@ -285,7 +285,8 @@ int write_archive_entries(struct archiver_args *args,
                git_attr_set_direction(GIT_ATTR_INDEX);
        }
 
-       err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
+       err = read_tree_recursive(args->repo, args->tree, "",
+                                 0, 0, &args->pathspec,
                                  queue_or_write_archive_entry,
                                  &context);
        if (err == READ_TREE_RECURSIVE)
@@ -346,7 +347,8 @@ static int path_exists(struct archiver_args *args, const char *path)
        ctx.args = args;
        parse_pathspec(&ctx.pathspec, 0, 0, "", paths);
        ctx.pathspec.recursive = 1;
-       ret = read_tree_recursive(args->tree, "", 0, 0, &ctx.pathspec,
+       ret = read_tree_recursive(args->repo, args->tree, "",
+                                 0, 0, &ctx.pathspec,
                                  reject_entry, &ctx);
        clear_pathspec(&ctx.pathspec);
        return ret != 0;
@@ -391,12 +393,12 @@ static void parse_treeish_arg(const char **argv,
                int refnamelen = colon - name;
 
                if (!dwim_ref(name, refnamelen, &oid, &ref))
-                       die("no such ref: %.*s", refnamelen, name);
+                       die(_("no such ref: %.*s"), refnamelen, name);
                free(ref);
        }
 
        if (get_oid(name, &oid))
-               die("Not a valid object name");
+               die(_("not a valid object name: %s"), name);
 
        commit = lookup_commit_reference_gently(ar_args->repo, &oid, 1);
        if (commit) {
@@ -409,7 +411,7 @@ static void parse_treeish_arg(const char **argv,
 
        tree = parse_tree_indirect(&oid);
        if (tree == NULL)
-               die("not a tree object");
+               die(_("not a tree object: %s"), oid_to_hex(&oid));
 
        if (prefix) {
                struct object_id tree_oid;
@@ -419,7 +421,7 @@ static void parse_treeish_arg(const char **argv,
                err = get_tree_entry(&tree->object.oid, prefix, &tree_oid,
                                     &mode);
                if (err || !S_ISDIR(mode))
-                       die("current working directory is untracked");
+                       die(_("current working directory is untracked"));
 
                tree = parse_tree_indirect(&tree_oid);
        }
diff --git a/attr.c b/attr.c
index eaece6658d64b2129332ffab39e30485730f3306..0cba78912907e35a26e43156e124912467e46b27 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -366,8 +366,8 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
        if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
            starts_with(name, ATTRIBUTE_MACRO_PREFIX)) {
                if (!macro_ok) {
-                       fprintf(stderr, "%s not allowed: %s:%d\n",
-                               name, src, lineno);
+                       fprintf_ln(stderr, _("%s not allowed: %s:%d"),
+                                  name, src, lineno);
                        goto fail_return;
                }
                is_macro = 1;
@@ -1092,7 +1092,7 @@ static void collect_some_attrs(const struct index_state *istate,
                               const char *path,
                               struct attr_check *check)
 {
-       int i, pathlen, rem, dirlen;
+       int pathlen, rem, dirlen;
        const char *cp, *last_slash = NULL;
        int basename_offset;
 
@@ -1113,20 +1113,6 @@ static void collect_some_attrs(const struct index_state *istate,
        all_attrs_init(&g_attr_hashmap, check);
        determine_macros(check->all_attrs, check->stack);
 
-       if (check->nr) {
-               rem = 0;
-               for (i = 0; i < check->nr; i++) {
-                       int n = check->items[i].attr->attr_nr;
-                       struct all_attrs_item *item = &check->all_attrs[n];
-                       if (item->macro) {
-                               item->value = ATTR__UNSET;
-                               rem++;
-                       }
-               }
-               if (rem == check->nr)
-                       return;
-       }
-
        rem = check->all_attrs_nr;
        fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
 }
index 28f5937035b762ad9fb091ea8fb7cc96f38fc92d..447af24807f49f5df4eace6435acba721745fc2c 100644 (file)
--- a/banned.h
+++ b/banned.h
@@ -16,6 +16,8 @@
 #define strcat(x,y) BANNED(strcat)
 #undef strncpy
 #define strncpy(x,y,n) BANNED(strncpy)
+#undef strncat
+#define strncat(x,y,n) BANNED(strncat)
 
 #undef sprintf
 #undef vsprintf
index 487675c67249a3164294aae71c1c2a650f3c4947..6bf521138a590496a7b3fa7c2515ac53f815a1e3 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -558,7 +558,8 @@ struct commit_list *filter_skipped(struct commit_list *list,
  * is increased by one between each call, but that should not matter
  * for this application.
  */
-static unsigned get_prn(unsigned count) {
+static unsigned get_prn(unsigned count)
+{
        count = count * 1103515245 + 12345;
        return (count/65536) % PRN_MODULO;
 }
@@ -626,14 +627,15 @@ static struct commit_list *managed_skipped(struct commit_list *list,
        return skip_away(list, count);
 }
 
-static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
+static void bisect_rev_setup(struct repository *r, struct rev_info *revs,
+                            const char *prefix,
                             const char *bad_format, const char *good_format,
                             int read_paths)
 {
        struct argv_array rev_argv = ARGV_ARRAY_INIT;
        int i;
 
-       repo_init_revisions(the_repository, revs, prefix);
+       repo_init_revisions(r, revs, prefix);
        revs->abbrev = 0;
        revs->commit_format = CMIT_FMT_UNSPECIFIED;
 
@@ -723,23 +725,25 @@ static int bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
        return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
 }
 
-static struct commit *get_commit_reference(const struct object_id *oid)
+static struct commit *get_commit_reference(struct repository *r,
+                                          const struct object_id *oid)
 {
-       struct commit *r = lookup_commit_reference(the_repository, oid);
-       if (!r)
+       struct commit *c = lookup_commit_reference(r, oid);
+       if (!c)
                die(_("Not a valid commit name %s"), oid_to_hex(oid));
-       return r;
+       return c;
 }
 
-static struct commit **get_bad_and_good_commits(int *rev_nr)
+static struct commit **get_bad_and_good_commits(struct repository *r,
+                                               int *rev_nr)
 {
        struct commit **rev;
        int i, n = 0;
 
        ALLOC_ARRAY(rev, 1 + good_revs.nr);
-       rev[n++] = get_commit_reference(current_bad_oid);
+       rev[n++] = get_commit_reference(r, current_bad_oid);
        for (i = 0; i < good_revs.nr; i++)
-               rev[n++] = get_commit_reference(good_revs.oid + i);
+               rev[n++] = get_commit_reference(r, good_revs.oid + i);
        *rev_nr = n;
 
        return rev;
@@ -823,12 +827,13 @@ static void check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
        free_commit_list(result);
 }
 
-static int check_ancestors(int rev_nr, struct commit **rev, const char *prefix)
+static int check_ancestors(struct repository *r, int rev_nr,
+                          struct commit **rev, const char *prefix)
 {
        struct rev_info revs;
        int res;
 
-       bisect_rev_setup(&revs, prefix, "^%s", "%s", 0);
+       bisect_rev_setup(r, &revs, prefix, "^%s", "%s", 0);
 
        bisect_common(&revs);
        res = (revs.commits != NULL);
@@ -847,7 +852,9 @@ static int check_ancestors(int rev_nr, struct commit **rev, const char *prefix)
  * If a merge base must be tested by the user, its source code will be
  * checked out to be tested by the user and we will exit.
  */
-static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
+static void check_good_are_ancestors_of_bad(struct repository *r,
+                                           const char *prefix,
+                                           int no_checkout)
 {
        char *filename = git_pathdup("BISECT_ANCESTORS_OK");
        struct stat st;
@@ -866,8 +873,8 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
                goto done;
 
        /* Check if all good revs are ancestor of the bad rev. */
-       rev = get_bad_and_good_commits(&rev_nr);
-       if (check_ancestors(rev_nr, rev, prefix))
+       rev = get_bad_and_good_commits(r, &rev_nr);
+       if (check_ancestors(r, rev_nr, rev, prefix))
                check_merge_bases(rev_nr, rev, no_checkout);
        free(rev);
 
@@ -885,12 +892,14 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
 /*
  * This does "git diff-tree --pretty COMMIT" without one fork+exec.
  */
-static void show_diff_tree(const char *prefix, struct commit *commit)
+static void show_diff_tree(struct repository *r,
+                          const char *prefix,
+                          struct commit *commit)
 {
        struct rev_info opt;
 
        /* diff-tree init */
-       repo_init_revisions(the_repository, &opt, prefix);
+       repo_init_revisions(r, &opt, prefix);
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        opt.abbrev = 0;
        opt.diff = 1;
@@ -945,7 +954,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
  * If no_checkout is non-zero, the bisection process does not
  * checkout the trial commit but instead simply updates BISECT_HEAD.
  */
-int bisect_next_all(const char *prefix, int no_checkout)
+int bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
 {
        struct rev_info revs;
        struct commit_list *tried;
@@ -957,9 +966,9 @@ int bisect_next_all(const char *prefix, int no_checkout)
        if (read_bisect_refs())
                die(_("reading bisect refs failed"));
 
-       check_good_are_ancestors_of_bad(prefix, no_checkout);
+       check_good_are_ancestors_of_bad(r, prefix, no_checkout);
 
-       bisect_rev_setup(&revs, prefix, "%s", "^%s", 1);
+       bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
        revs.limited = 1;
 
        bisect_common(&revs);
@@ -993,7 +1002,7 @@ int bisect_next_all(const char *prefix, int no_checkout)
                exit_if_skipped_commits(tried, current_bad_oid);
                printf("%s is the first %s commit\n", oid_to_hex(bisect_rev),
                        term_bad);
-               show_diff_tree(prefix, revs.commits->item);
+               show_diff_tree(r, prefix, revs.commits->item);
                /* This means the bisection process succeeded. */
                exit(10);
        }
index 34df20935123065b4b253288ea0e22b1721af0e9..821d16e4ddad71d177190a0e05012c5a509fa17b 100644 (file)
--- a/bisect.h
+++ b/bisect.h
@@ -2,6 +2,7 @@
 #define BISECT_H
 
 struct commit_list;
+struct repository;
 
 /*
  * Find bisection. If something is found, `reaches` will be the number of
@@ -30,7 +31,9 @@ struct rev_list_info {
        const char *header_prefix;
 };
 
-extern int bisect_next_all(const char *prefix, int no_checkout);
+extern int bisect_next_all(struct repository *r,
+                          const char *prefix,
+                          int no_checkout);
 
 extern int estimate_bisect_steps(int all);
 
diff --git a/blame.c b/blame.c
index d84c937780801f51b8eb98b7e367c8017701ba5c..43861437f749849e85d448eba692b42488ec459d 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -116,35 +116,38 @@ static void verify_working_tree_path(struct repository *r,
                die("no such path '%s' in HEAD", path);
 }
 
-static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)
+static struct commit_list **append_parent(struct repository *r,
+                                         struct commit_list **tail,
+                                         const struct object_id *oid)
 {
        struct commit *parent;
 
-       parent = lookup_commit_reference(the_repository, oid);
+       parent = lookup_commit_reference(r, oid);
        if (!parent)
                die("no such commit %s", oid_to_hex(oid));
        return &commit_list_insert(parent, tail)->next;
 }
 
-static void append_merge_parents(struct commit_list **tail)
+static void append_merge_parents(struct repository *r,
+                                struct commit_list **tail)
 {
        int merge_head;
        struct strbuf line = STRBUF_INIT;
 
-       merge_head = open(git_path_merge_head(the_repository), O_RDONLY);
+       merge_head = open(git_path_merge_head(r), O_RDONLY);
        if (merge_head < 0) {
                if (errno == ENOENT)
                        return;
                die("cannot open '%s' for reading",
-                   git_path_merge_head(the_repository));
+                   git_path_merge_head(r));
        }
 
        while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
                struct object_id oid;
                if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
                        die("unknown line in '%s': %s",
-                           git_path_merge_head(the_repository), line.buf);
-               tail = append_parent(tail, &oid);
+                           git_path_merge_head(r), line.buf);
+               tail = append_parent(r, tail, &oid);
        }
        close(merge_head);
        strbuf_release(&line);
@@ -155,11 +158,13 @@ static void append_merge_parents(struct commit_list **tail)
  * want to transfer ownership of the buffer to the commit (so we
  * must use detach).
  */
-static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
+static void set_commit_buffer_from_strbuf(struct repository *r,
+                                         struct commit *c,
+                                         struct strbuf *sb)
 {
        size_t len;
        void *buf = strbuf_detach(sb, &len);
-       set_commit_buffer(the_repository, c, buf, len);
+       set_commit_buffer(r, c, buf, len);
 }
 
 /*
@@ -185,7 +190,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,
 
        read_index(r->index);
        time(&now);
-       commit = alloc_commit_node(the_repository);
+       commit = alloc_commit_node(r);
        commit->object.parsed = 1;
        commit->date = now;
        parent_tail = &commit->parents;
@@ -193,8 +198,8 @@ static struct commit *fake_working_tree_commit(struct repository *r,
        if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
                die("no such ref: HEAD");
 
-       parent_tail = append_parent(parent_tail, &head_oid);
-       append_merge_parents(parent_tail);
+       parent_tail = append_parent(r, parent_tail, &head_oid);
+       append_merge_parents(r, parent_tail);
        verify_working_tree_path(r, commit, path);
 
        origin = make_origin(commit, path);
@@ -211,7 +216,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,
                    ident, ident, path,
                    (!contents_from ? path :
                     (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
-       set_commit_buffer_from_strbuf(commit, &msg);
+       set_commit_buffer_from_strbuf(r, commit, &msg);
 
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
@@ -1678,7 +1683,7 @@ static struct commit *find_single_final(struct rev_info *revs,
                struct object *obj = revs->pending.objects[i].item;
                if (obj->flags & UNINTERESTING)
                        continue;
-               obj = deref_tag(the_repository, obj, NULL, 0);
+               obj = deref_tag(revs->repo, obj, NULL, 0);
                if (obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (found)
@@ -1709,14 +1714,14 @@ static struct commit *dwim_reverse_initial(struct rev_info *revs,
 
        /* Is that sole rev a committish? */
        obj = revs->pending.objects[0].item;
-       obj = deref_tag(the_repository, obj, NULL, 0);
+       obj = deref_tag(revs->repo, obj, NULL, 0);
        if (obj->type != OBJ_COMMIT)
                return NULL;
 
        /* Do we have HEAD? */
        if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
                return NULL;
-       head_commit = lookup_commit_reference_gently(the_repository,
+       head_commit = lookup_commit_reference_gently(revs->repo,
                                                     &head_oid, 1);
        if (!head_commit)
                return NULL;
@@ -1745,7 +1750,7 @@ static struct commit *find_single_initial(struct rev_info *revs,
                struct object *obj = revs->pending.objects[i].item;
                if (!(obj->flags & UNINTERESTING))
                        continue;
-               obj = deref_tag(the_repository, obj, NULL, 0);
+               obj = deref_tag(revs->repo, obj, NULL, 0);
                if (obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (found)
index 776f55fc66fb7d782f30b05ca9bdff4a55028f49..28b81a7e0256b2923c0fe296d9618d130821cdc4 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -242,7 +242,8 @@ N_("\n"
 "will track its remote counterpart, you may want to use\n"
 "\"git push -u\" to set the upstream config as you push.");
 
-void create_branch(const char *name, const char *start_name,
+void create_branch(struct repository *r,
+                  const char *name, const char *start_name,
                   int force, int clobber_head_ok, int reflog,
                   int quiet, enum branch_track track)
 {
@@ -300,7 +301,7 @@ void create_branch(const char *name, const char *start_name,
                break;
        }
 
-       if ((commit = lookup_commit_reference(the_repository, &oid)) == NULL)
+       if ((commit = lookup_commit_reference(r, &oid)) == NULL)
                die(_("Not a valid branch point: '%s'."), start_name);
        oidcpy(&oid, &commit->object.oid);
 
@@ -336,15 +337,15 @@ void create_branch(const char *name, const char *start_name,
        free(real_ref);
 }
 
-void remove_branch_state(void)
+void remove_branch_state(struct repository *r)
 {
-       unlink(git_path_cherry_pick_head(the_repository));
-       unlink(git_path_revert_head(the_repository));
-       unlink(git_path_merge_head(the_repository));
-       unlink(git_path_merge_rr(the_repository));
-       unlink(git_path_merge_msg(the_repository));
-       unlink(git_path_merge_mode(the_repository));
-       unlink(git_path_squash_msg(the_repository));
+       unlink(git_path_cherry_pick_head(r));
+       unlink(git_path_revert_head(r));
+       unlink(git_path_merge_head(r));
+       unlink(git_path_merge_rr(r));
+       unlink(git_path_merge_msg(r));
+       unlink(git_path_merge_mode(r));
+       unlink(git_path_squash_msg(r));
 }
 
 void die_if_checked_out(const char *branch, int ignore_current_worktree)
index 5cace4581fe43aa5fdc20b4315cee6eff1034003..29c1afa4d0ce5d1639b81d3d652d5b16085218e9 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -1,6 +1,7 @@
 #ifndef BRANCH_H
 #define BRANCH_H
 
+struct repository;
 struct strbuf;
 
 enum branch_track {
@@ -19,6 +20,8 @@ extern enum branch_track git_branch_track;
 /*
  * Creates a new branch, where:
  *
+ *   - r is the repository to add a branch to
+ *
  *   - name is the new branch name
  *
  *   - start_name is the name of the existing branch that the new branch should
@@ -37,7 +40,8 @@ extern enum branch_track git_branch_track;
  *     that start_name is a tracking branch for (if any).
  *
  */
-void create_branch(const char *name, const char *start_name,
+void create_branch(struct repository *r,
+                  const char *name, const char *start_name,
                   int force, int clobber_head_ok,
                   int reflog, int quiet, enum branch_track track);
 
@@ -60,7 +64,7 @@ extern int validate_new_branchname(const char *name, struct strbuf *ref, int for
  * Remove information about the state of working on the current
  * branch. (E.g., MERGE_HEAD)
  */
-void remove_branch_state(void);
+void remove_branch_state(struct repository *r);
 
 /*
  * Configure local branch "local" as downstream to branch "remote"
index f65c1722993b62e93feb85e506dffbc77b7ce921..7c2a7c5a4d4aa619e81ef61a69b619b7c96b8ac5 100644 (file)
@@ -137,7 +137,7 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
                        continue; /* do not touch non blobs */
                if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
                        continue;
-               retval |= add_file_to_cache(ce->name, flags | HASH_RENORMALIZE);
+               retval |= add_file_to_cache(ce->name, flags | ADD_CACHE_RENORMALIZE);
        }
 
        return retval;
@@ -176,7 +176,7 @@ static void refresh(int verbose, const struct pathspec *pathspec)
                        die(_("pathspec '%s' did not match any files"),
                            pathspec->items[i].match);
        }
-        free(seen);
+       free(seen);
 }
 
 int run_add_interactive(const char *revision, const char *patch_mode,
@@ -239,7 +239,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
        rev.diffopt.output_format = DIFF_FORMAT_PATCH;
        rev.diffopt.use_color = 0;
        rev.diffopt.flags.ignore_dirty_submodules = 1;
-       out = open(file, O_CREAT | O_WRONLY, 0666);
+       out = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
        if (out < 0)
                die(_("Could not open '%s' for writing."), file);
        rev.diffopt.file = xfdopen(out, "w");
index 310eefe9e8e74a529e578d842b8d376a0afc0d8e..0cc399729205a3adfc8afe0d211bdba13e1a3977 100644 (file)
@@ -1954,7 +1954,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
        if (merge_tree(remote_tree))
                return -1;
 
-       remove_branch_state();
+       remove_branch_state(the_repository);
 
        return 0;
 }
@@ -1965,7 +1965,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
 static void am_rerere_clear(void)
 {
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
-       rerere_clear(&merge_rr);
+       rerere_clear(the_repository, &merge_rr);
        string_list_clear(&merge_rr, 1);
 }
 
@@ -1984,6 +1984,15 @@ static void am_skip(struct am_state *state)
        if (clean_index(&head, &head))
                die(_("failed to clean index"));
 
+       if (state->rebasing) {
+               FILE *fp = xfopen(am_path(state, "rewritten"), "a");
+
+               assert(!is_null_oid(&state->orig_commit));
+               fprintf(fp, "%s ", oid_to_hex(&state->orig_commit));
+               fprintf(fp, "%s\n", oid_to_hex(&head));
+               fclose(fp);
+       }
+
        am_next(state);
        am_load(state);
        am_run(state, 0);
index d2455237ce04d68de624ed60157dc90a4ee4e477..45d11669aae459caf95bb6b4737558297debae89 100644 (file)
@@ -27,10 +27,10 @@ static int run_remote_archiver(int argc, const char **argv,
                               const char *remote, const char *exec,
                               const char *name_hint)
 {
-       char *buf;
        int fd[2], i, rv;
        struct transport *transport;
        struct remote *_remote;
+       struct packet_reader reader;
 
        _remote = remote_get(remote);
        if (!_remote->url[0])
@@ -53,18 +53,19 @@ static int run_remote_archiver(int argc, const char **argv,
                packet_write_fmt(fd[1], "argument %s\n", argv[i]);
        packet_flush(fd[1]);
 
-       buf = packet_read_line(fd[0], NULL);
-       if (!buf)
+       packet_reader_init(&reader, fd[0], NULL, 0,
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
+
+       if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
                die(_("git archive: expected ACK/NAK, got a flush packet"));
-       if (strcmp(buf, "ACK")) {
-               if (starts_with(buf, "NACK "))
-                       die(_("git archive: NACK %s"), buf + 5);
-               if (starts_with(buf, "ERR "))
-                       die(_("remote error: %s"), buf + 4);
+       if (strcmp(reader.line, "ACK")) {
+               if (starts_with(reader.line, "NACK "))
+                       die(_("git archive: NACK %s"), reader.line + 5);
                die(_("git archive: protocol error"));
        }
 
-       if (packet_read_line(fd[0], NULL))
+       if (packet_reader_read(&reader) != PACKET_READ_FLUSH)
                die(_("git archive: expected a flush"));
 
        /* Now, start reading from fd[0] and spit it out to stdout */
index 22e669e3b170f96c046a2aa4aa2c289a0599b6f0..c1cff3266114323f466133b98050f5aec71c5f91 100644 (file)
@@ -660,7 +660,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 
        switch (cmdmode) {
        case NEXT_ALL:
-               return bisect_next_all(prefix, no_checkout);
+               return bisect_next_all(the_repository, prefix, no_checkout);
        case WRITE_TERMS:
                if (argc != 2)
                        return error(_("--write-terms requires two arguments"));
index 0c55f7f065d6ceb34d27a165160192fabe9f22d1..1be727209b7ceaead46d0bc5e4b9666fbe7ef7a1 100644 (file)
@@ -783,7 +783,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                 * create_branch takes care of setting up the tracking
                 * info and making sure new_upstream is correct
                 */
-               create_branch(branch->name, new_upstream, 0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE);
+               create_branch(the_repository, branch->name, new_upstream,
+                             0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE);
        } else if (unset_upstream) {
                struct branch *branch = branch_get(argv[0]);
                struct strbuf buf = STRBUF_INIT;
@@ -814,7 +815,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                if (track == BRANCH_TRACK_OVERRIDE)
                        die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
 
-               create_branch(argv[0], (argc == 2) ? argv[1] : head,
+               create_branch(the_repository,
+                             argv[0], (argc == 2) ? argv[1] : head,
                              force, 0, reflog, quiet, track);
 
        } else
index d0de59b94ff23eebe03984dc670babf793bdd5ff..9e9c65d9c698cae2ab735463edf378d5dd3a9815 100644 (file)
@@ -40,7 +40,7 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
                        usage(builtin_bundle_usage);
                        return 1;
                }
-               if (verify_bundle(&header, 1))
+               if (verify_bundle(the_repository, &header, 1))
                        return 1;
                fprintf(stderr, _("%s is okay\n"), bundle_file);
                return 0;
@@ -56,11 +56,12 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
                }
                if (!startup_info->have_repository)
                        die(_("Need a repository to create a bundle."));
-               return !!create_bundle(&header, bundle_file, argc, argv);
+               return !!create_bundle(the_repository, &header,
+                                      bundle_file, argc, argv);
        } else if (!strcmp(cmd, "unbundle")) {
                if (!startup_info->have_repository)
                        die(_("Need a repository to unbundle."));
-               return !!unbundle(&header, bundle_fd, 0) ||
+               return !!unbundle(the_repository, &header, bundle_fd, 0) ||
                        list_bundle_refs(&header, argc, argv);
        } else
                usage(builtin_bundle_usage);
index 2ca56fd086bf6bef7595ad008d9eda2c64901cf5..cebc6d7f8a197495e7c87f6b2430f340dc224891 100644 (file)
@@ -380,7 +380,7 @@ static void batch_one_object(const char *obj_name,
 {
        struct object_context ctx;
        int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0;
-       enum follow_symlinks_result result;
+       enum get_oid_result result;
 
        result = get_oid_with_context(obj_name, flags, &data->oid, &ctx);
        if (result != FOUND) {
@@ -388,6 +388,9 @@ static void batch_one_object(const char *obj_name,
                case MISSING_OBJECT:
                        printf("%s missing\n", obj_name);
                        break;
+               case SHORT_NAME_AMBIGUOUS:
+                       printf("%s ambiguous\n", obj_name);
+                       break;
                case DANGLING_SYMLINK:
                        printf("dangling %"PRIuMAX"\n%s\n",
                               (uintmax_t)strlen(obj_name), obj_name);
index eb74774cbc9d6bd6acf1b7fe2f31b8e80e461a12..a2a726ad8d8fa65d2fbcd1b111d71bad61c5cee8 100644 (file)
@@ -67,7 +67,8 @@ static int checkout_file(const char *name, const char *prefix)
                        continue;
                did_checkout = 1;
                if (checkout_entry(ce, &state,
-                   to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
+                                  to_tempfile ? topath[ce_stage(ce)] : NULL,
+                                  NULL) < 0)
                        errs++;
        }
 
@@ -111,7 +112,8 @@ static void checkout_all(const char *prefix, int prefix_length)
                                write_tempfile_record(last_ce->name, prefix);
                }
                if (checkout_entry(ce, &state,
-                   to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
+                                  to_tempfile ? topath[ce_stage(ce)] : NULL,
+                                  NULL) < 0)
                        errs++;
                last_ce = ce;
        }
index acdafc6e4c4d104aadcbe7492a8045e16e83ba50..9f8f3466f66d8845a60719cc608375b71679d4b4 100644 (file)
@@ -44,6 +44,7 @@ struct checkout_opts {
        int ignore_skipworktree;
        int ignore_other_worktrees;
        int show_progress;
+       int count_checkout_paths;
        /*
         * If new checkout options are added, skip_merge_working_tree
         * should be updated accordingly.
@@ -115,7 +116,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 
 static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
 {
-       read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL);
+       read_tree_recursive(the_repository, tree, "", 0, 0,
+                           pathspec, update_some, NULL);
 
        /* update the index with the given tree's info
         * for all args, expanding wildcards, and exit
@@ -165,12 +167,13 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
 }
 
 static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
-                         const struct checkout *state)
+                         const struct checkout *state, int *nr_checkouts)
 {
        while (pos < active_nr &&
               !strcmp(active_cache[pos]->name, ce->name)) {
                if (ce_stage(active_cache[pos]) == stage)
-                       return checkout_entry(active_cache[pos], state, NULL);
+                       return checkout_entry(active_cache[pos], state,
+                                             NULL, nr_checkouts);
                pos++;
        }
        if (stage == 2)
@@ -179,7 +182,7 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
                return error(_("path '%s' does not have their version"), ce->name);
 }
 
-static int checkout_merged(int pos, const struct checkout *state)
+static int checkout_merged(int pos, const struct checkout *state, int *nr_checkouts)
 {
        struct cache_entry *ce = active_cache[pos];
        const char *path = ce->name;
@@ -242,7 +245,7 @@ static int checkout_merged(int pos, const struct checkout *state)
        ce = make_transient_cache_entry(mode, &oid, path, 2);
        if (!ce)
                die(_("make_cache_entry failed for path '%s'"), path);
-       status = checkout_entry(ce, state, NULL);
+       status = checkout_entry(ce, state, NULL, nr_checkouts);
        discard_cache_entry(ce);
        return status;
 }
@@ -257,6 +260,7 @@ static int checkout_paths(const struct checkout_opts *opts,
        struct commit *head;
        int errs = 0;
        struct lock_file lock_file = LOCK_INIT;
+       int nr_checkouts = 0;
 
        if (opts->track != BRANCH_TRACK_UNSPECIFIED)
                die(_("'%s' cannot be used with updating paths"), "--track");
@@ -371,17 +375,36 @@ static int checkout_paths(const struct checkout_opts *opts,
                struct cache_entry *ce = active_cache[pos];
                if (ce->ce_flags & CE_MATCHED) {
                        if (!ce_stage(ce)) {
-                               errs |= checkout_entry(ce, &state, NULL);
+                               errs |= checkout_entry(ce, &state,
+                                                      NULL, &nr_checkouts);
                                continue;
                        }
                        if (opts->writeout_stage)
-                               errs |= checkout_stage(opts->writeout_stage, ce, pos, &state);
+                               errs |= checkout_stage(opts->writeout_stage,
+                                                      ce, pos,
+                                                      &state, &nr_checkouts);
                        else if (opts->merge)
-                               errs |= checkout_merged(pos, &state);
+                               errs |= checkout_merged(pos, &state,
+                                                       &nr_checkouts);
                        pos = skip_same_name(ce, pos) - 1;
                }
        }
-       errs |= finish_delayed_checkout(&state);
+       errs |= finish_delayed_checkout(&state, &nr_checkouts);
+
+       if (opts->count_checkout_paths) {
+               if (opts->source_tree)
+                       fprintf_ln(stderr, Q_("Checked out %d path out of %s",
+                                             "Checked out %d paths out of %s",
+                                             nr_checkouts),
+                                  nr_checkouts,
+                                  find_unique_abbrev(&opts->source_tree->object.oid,
+                                                     DEFAULT_ABBREV));
+               else
+                       fprintf_ln(stderr, Q_("Checked out %d path out of the index",
+                                             "Checked out %d paths out of the index",
+                                             nr_checkouts),
+                                  nr_checkouts);
+       }
 
        if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
@@ -569,6 +592,14 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,
         * Remaining variables are not checkout options but used to track state
         */
 
+        /*
+         * Do the merge if this is the initial checkout. We cannot use
+         * is_cache_unborn() here because the index hasn't been loaded yet
+         * so cache_nr and timestamp.sec are always zero.
+         */
+       if (!file_exists(get_index_file()))
+               return 0;
+
        return 1;
 }
 
@@ -753,7 +784,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                        free(refname);
                }
                else
-                       create_branch(opts->new_branch, new_branch_info->name,
+                       create_branch(the_repository,
+                                     opts->new_branch, new_branch_info->name,
                                      opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_log,
@@ -811,7 +843,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                                delete_reflog(old_branch_info->path);
                }
        }
-       remove_branch_state();
+       remove_branch_state(the_repository);
        strbuf_release(&msg);
        if (!opts->quiet &&
            (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
@@ -1064,6 +1096,7 @@ static int parse_branchname_arg(int argc, const char **argv,
                has_dash_dash = 1; /* case (3) or (1) */
        else if (dash_dash_pos >= 2)
                die(_("only one reference expected, %d given."), dash_dash_pos);
+       opts->count_checkout_paths = !opts->quiet && !has_dash_dash;
 
        if (!strcmp(arg, "-"))
                arg = "@{-1}";
@@ -1079,9 +1112,12 @@ static int parse_branchname_arg(int argc, const char **argv,
                 */
                int recover_with_dwim = dwim_new_local_branch_ok;
 
-               if (!has_dash_dash &&
-                   (check_filename(opts->prefix, arg) || !no_wildcard(arg)))
+               int could_be_checkout_paths = !has_dash_dash &&
+                       check_filename(opts->prefix, arg);
+
+               if (!has_dash_dash && !no_wildcard(arg))
                        recover_with_dwim = 0;
+
                /*
                 * Accept "git checkout foo" and "git checkout foo --"
                 * as candidates for dwim.
@@ -1094,6 +1130,10 @@ static int parse_branchname_arg(int argc, const char **argv,
                        const char *remote = unique_tracking_name(arg, rev,
                                                                  dwim_remotes_matched);
                        if (remote) {
+                               if (could_be_checkout_paths)
+                                       die(_("'%s' could be both a local file and a tracking branch.\n"
+                                             "Please use -- (and optionally --no-guess) to disambiguate"),
+                                           arg);
                                *new_branch = arg;
                                arg = remote;
                                /* DWIMmed to create local branch, case (3).(b) */
@@ -1228,7 +1268,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        struct checkout_opts opts;
        struct branch_info new_branch_info;
        char *conflict_style = NULL;
-       int dwim_new_local_branch = 1;
+       int dwim_new_local_branch, no_dwim_new_local_branch = 0;
        int dwim_remotes_matched = 0;
        struct option options[] = {
                OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
@@ -1258,8 +1298,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
                OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
                         N_("do not limit pathspecs to sparse entries only")),
-               OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
-                               N_("second guess 'git checkout <no-such-branch>'")),
+               OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
+                        N_("do not second guess 'git checkout <no-such-branch>'")),
                OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
                         N_("do not check if another worktree is holding the given ref")),
                { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
@@ -1282,6 +1322,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
+       dwim_new_local_branch = !no_dwim_new_local_branch;
        if (opts.show_progress < 0) {
                if (opts.quiet)
                        opts.show_progress = 0;
index 15b142d64640e29c10e62d565ac21adbaaeebca4..739de68820c52a3d24f8f256bb36ed72877740b6 100644 (file)
@@ -548,7 +548,7 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch
 }
 
 static struct ref *wanted_peer_refs(const struct ref *refs,
-               struct refspec_item *refspec)
+               struct refspec *refspec)
 {
        struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
        struct ref *local_refs = head;
@@ -569,13 +569,19 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
                        warning(_("Could not find remote branch %s to clone."),
                                option_branch);
                else {
-                       get_fetch_map(remote_head, refspec, &tail, 0);
+                       int i;
+                       for (i = 0; i < refspec->nr; i++)
+                               get_fetch_map(remote_head, &refspec->items[i],
+                                             &tail, 0);
 
                        /* if --branch=tag, pull the requested tag explicitly */
                        get_fetch_map(remote_head, tag_refspec, &tail, 0);
                }
-       } else
-               get_fetch_map(refs, refspec, &tail, 0);
+       } else {
+               int i;
+               for (i = 0; i < refspec->nr; i++)
+                       get_fetch_map(refs, &refspec->items[i], &tail, 0);
+       }
 
        if (!option_mirror && !option_single_branch && !option_no_tags)
                get_fetch_map(refs, tag_refspec, &tail, 0);
@@ -890,7 +896,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        const struct ref *our_head_points_at;
        struct ref *mapped_refs;
        const struct ref *ref;
-       struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
+       struct strbuf key = STRBUF_INIT;
+       struct strbuf default_refspec = STRBUF_INIT;
        struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
        struct transport *transport = NULL;
        const char *src_ref_prefix = "refs/heads/";
@@ -898,7 +905,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        int err = 0, complete_refs_before_fetch = 1;
        int submodule_progress;
 
-       struct refspec rs = REFSPEC_INIT_FETCH;
        struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
 
        fetch_if_missing = 0;
@@ -1067,7 +1073,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
        }
 
-       strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);
        strbuf_addf(&key, "remote.%s.url", option_origin);
        git_config_set(key.buf, repo);
        strbuf_reset(&key);
@@ -1081,11 +1086,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_required_reference.nr || option_optional_reference.nr)
                setup_reference();
 
-       refspec_append(&rs, value.buf);
+       remote = remote_get(option_origin);
 
-       strbuf_reset(&value);
+       strbuf_addf(&default_refspec, "+%s*:%s*", src_ref_prefix,
+                   branch_top.buf);
+       refspec_append(&remote->fetch, default_refspec.buf);
 
-       remote = remote_get(option_origin);
        transport = transport_get(remote, remote->url[0]);
        transport_set_verbosity(transport, option_verbosity, option_progress);
        transport->family = family;
@@ -1130,9 +1136,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                                     option_upload_pack);
 
        if (filter_options.choice) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
+               expand_list_objects_filter_spec(&filter_options,
+                                               &expanded_filter_spec);
                transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
-                                    filter_options.filter_spec);
+                                    expanded_filter_spec.buf);
                transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+               strbuf_release(&expanded_filter_spec);
        }
 
        if (transport->smart_options && !deepen && !filter_options.choice)
@@ -1140,7 +1150,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 
        argv_array_push(&ref_prefixes, "HEAD");
-       refspec_ref_prefixes(&rs, &ref_prefixes);
+       refspec_ref_prefixes(&remote->fetch, &ref_prefixes);
        if (option_branch)
                expand_ref_prefix(&ref_prefixes, option_branch);
        if (!option_no_tags)
@@ -1149,7 +1159,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        refs = transport_get_remote_refs(transport, &ref_prefixes);
 
        if (refs) {
-               mapped_refs = wanted_peer_refs(refs, &rs.items[0]);
+               mapped_refs = wanted_peer_refs(refs, &remote->fetch);
                /*
                 * transport_get_remote_refs() may return refs with null sha-1
                 * in mapped_refs (see struct transport->get_refs_list
@@ -1240,10 +1250,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        strbuf_release(&reflog_msg);
        strbuf_release(&branch_top);
        strbuf_release(&key);
-       strbuf_release(&value);
+       strbuf_release(&default_refspec);
        junk_mode = JUNK_LEAVE_ALL;
 
-       refspec_clear(&rs);
        argv_array_clear(&ref_prefixes);
        return err;
 }
index c02a3f1221d625ad6407317199cc35c7a56a09ba..4ae502754c292d0e743ed8fbfdf9d5dda00d1dba 100644 (file)
@@ -110,8 +110,8 @@ static int graph_read(int argc, const char **argv)
                printf(" oid_lookup");
        if (graph->chunk_commit_data)
                printf(" commit_metadata");
-       if (graph->chunk_large_edges)
-               printf(" large_edges");
+       if (graph->chunk_extra_edges)
+               printf(" extra_edges");
        printf("\n");
 
        UNLEAK(graph);
index c021b119bb9df106ab7a860e032ea5dd35529401..7d2e0b61e5fc73a9b338a4f37b9a51ddc6cbf306 100644 (file)
@@ -188,7 +188,7 @@ static void determine_whence(struct wt_status *s)
 
 static void status_init_config(struct wt_status *s, config_fn_t fn)
 {
-       wt_status_prepare(s);
+       wt_status_prepare(the_repository, s);
        init_diff_ui_defaults();
        git_config(fn, s);
        determine_whence(s);
@@ -351,7 +351,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                if (write_locked_index(&the_index, &index_lock, 0))
                        die(_("unable to create temporary index"));
 
-               old_index_env = getenv(INDEX_ENVIRONMENT);
+               old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
                setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1);
 
                if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
@@ -361,6 +361,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                        setenv(INDEX_ENVIRONMENT, old_index_env, 1);
                else
                        unsetenv(INDEX_ENVIRONMENT);
+               FREE_AND_NULL(old_index_env);
 
                discard_cache();
                read_cache_from(get_lock_file_path(&index_lock));
@@ -911,7 +912,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                        if (ignore_submodule_arg &&
                            !strcmp(ignore_submodule_arg, "all"))
                                flags.ignore_submodules = 1;
-                       committable = index_differs_from(parent, &flags, 1);
+                       committable = index_differs_from(the_repository,
+                                                        parent, &flags, 1);
                }
        }
        strbuf_release(&committer_ident);
@@ -1682,7 +1684,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                        flags |= SUMMARY_INITIAL_COMMIT;
                if (author_date_is_interesting())
                        flags |= SUMMARY_SHOW_AUTHOR_DATE;
-               print_commit_summary(prefix, &oid, flags);
+               print_commit_summary(the_repository, prefix,
+                                    &oid, flags);
        }
 
        UNLEAK(err);
index 84385ef165195e7c65be54e9463cb49578bec2bb..98d65bc0ad4fd4bee8bd2755b9d7444d9862eae8 100644 (file)
@@ -164,7 +164,8 @@ static NORETURN void usage_builtin_config(void)
        usage_with_options(builtin_config_usage, builtin_config_options);
 }
 
-static void check_argc(int argc, int min, int max) {
+static void check_argc(int argc, int min, int max)
+{
        if (argc >= min && argc <= max)
                return;
        if (min == max)
@@ -598,7 +599,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        int nongit = !startup_info->have_repository;
        char *value;
 
-       given_config_source.file = getenv(CONFIG_ENVIRONMENT);
+       given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
 
        argc = parse_options(argc, argv, prefix, builtin_config_options,
                             builtin_config_usage,
index a7cad052c615807b819f32c999c161f0f905c1bc..3fae474f6f1f4fb78911ab4a26a160f04527d292 100644 (file)
@@ -78,10 +78,10 @@ static int count_cruft(const char *basename, const char *path, void *data)
        return 0;
 }
 
-static int print_alternate(struct alternate_object_database *alt, void *data)
+static int print_alternate(struct object_directory *odb, void *data)
 {
        printf("alternate: ");
-       quote_c_style(alt->path, NULL, stdout, 0);
+       quote_c_style(odb->path, NULL, stdout, 0);
        putchar('\n');
        return 0;
 }
index 544b0e86397cb98ddfdad3cffdd983905ee36029..71318c26e177e8aa4176201e4f23e6f43b63b630 100644 (file)
@@ -323,7 +323,7 @@ static int checkout_path(unsigned mode, struct object_id *oid,
        int ret;
 
        ce = make_transient_cache_entry(mode, oid, path, 0);
-       ret = checkout_entry(ce, state, NULL);
+       ret = checkout_entry(ce, state, NULL, NULL);
 
        discard_cache_entry(ce);
        return ret;
index 5790f0d554b0aed2ea36a38c289cc6ce2c3c0faf..9e283482efcfa6de0376cc9306061cea149b12df 100644 (file)
@@ -31,13 +31,16 @@ static const char *fast_export_usage[] = {
 };
 
 static int progress;
-static enum { ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = ABORT;
-static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ERROR;
+static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
+static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
 static int fake_missing_tagger;
 static int use_done_feature;
 static int no_data;
 static int full_tree;
+static int reference_excluded_commits;
+static int show_original_ids;
 static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
+static struct string_list tag_refs = STRING_LIST_INIT_NODUP;
 static struct refspec refspecs = REFSPEC_INIT_FETCH;
 static int anonymize;
 static struct revision_sources revision_sources;
@@ -46,7 +49,7 @@ static int parse_opt_signed_tag_mode(const struct option *opt,
                                     const char *arg, int unset)
 {
        if (unset || !strcmp(arg, "abort"))
-               signed_tag_mode = ABORT;
+               signed_tag_mode = SIGNED_TAG_ABORT;
        else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
                signed_tag_mode = VERBATIM;
        else if (!strcmp(arg, "warn"))
@@ -64,7 +67,7 @@ static int parse_opt_tag_of_filtered_mode(const struct option *opt,
                                          const char *arg, int unset)
 {
        if (unset || !strcmp(arg, "abort"))
-               tag_of_filtered_mode = ERROR;
+               tag_of_filtered_mode = TAG_FILTERING_ABORT;
        else if (!strcmp(arg, "drop"))
                tag_of_filtered_mode = DROP;
        else if (!strcmp(arg, "rewrite"))
@@ -187,6 +190,22 @@ static int get_object_mark(struct object *object)
        return ptr_to_mark(decoration);
 }
 
+static struct commit *rewrite_commit(struct commit *p)
+{
+       for (;;) {
+               if (p->parents && p->parents->next)
+                       break;
+               if (p->object.flags & UNINTERESTING)
+                       break;
+               if (!(p->object.flags & TREESAME))
+                       break;
+               if (!p->parents)
+                       return NULL;
+               p = p->parents->item;
+       }
+       return p;
+}
+
 static void show_progress(void)
 {
        static int counter = 0;
@@ -243,7 +262,7 @@ static void export_blob(const struct object_id *oid)
                if (!buf)
                        die("could not read blob %s", oid_to_hex(oid));
                if (check_object_signature(oid, buf, size, type_name(type)) < 0)
-                       die("sha1 mismatch in blob %s", oid_to_hex(oid));
+                       die("oid mismatch in blob %s", oid_to_hex(oid));
                object = parse_object_buffer(the_repository, oid, type,
                                             size, buf, &eaten);
        }
@@ -253,7 +272,10 @@ static void export_blob(const struct object_id *oid)
 
        mark_next_object(object);
 
-       printf("blob\nmark :%"PRIu32"\ndata %"PRIuMAX"\n", last_idnum, (uintmax_t)size);
+       printf("blob\nmark :%"PRIu32"\n", last_idnum);
+       if (show_original_ids)
+               printf("original-oid %s\n", oid_to_hex(oid));
+       printf("data %"PRIuMAX"\n", (uintmax_t)size);
        if (size && fwrite(buf, size, 1, stdout) != 1)
                die_errno("could not write blob '%s'", oid_to_hex(oid));
        printf("\n");
@@ -330,17 +352,18 @@ static void print_path(const char *path)
 
 static void *generate_fake_oid(const void *old, size_t *len)
 {
-       static uint32_t counter = 1; /* avoid null sha1 */
-       unsigned char *out = xcalloc(GIT_SHA1_RAWSZ, 1);
-       put_be32(out + GIT_SHA1_RAWSZ - 4, counter++);
+       static uint32_t counter = 1; /* avoid null oid */
+       const unsigned hashsz = the_hash_algo->rawsz;
+       unsigned char *out = xcalloc(hashsz, 1);
+       put_be32(out + hashsz - 4, counter++);
        return out;
 }
 
-static const unsigned char *anonymize_sha1(const struct object_id *oid)
+static const struct object_id *anonymize_oid(const struct object_id *oid)
 {
-       static struct hashmap sha1s;
-       size_t len = GIT_SHA1_RAWSZ;
-       return anonymize_mem(&sha1s, generate_fake_oid, oid, &len);
+       static struct hashmap objs;
+       size_t len = the_hash_algo->rawsz;
+       return anonymize_mem(&objs, generate_fake_oid, oid, &len);
 }
 
 static void show_filemodify(struct diff_queue_struct *q,
@@ -399,9 +422,9 @@ static void show_filemodify(struct diff_queue_struct *q,
                         */
                        if (no_data || S_ISGITLINK(spec->mode))
                                printf("M %06o %s ", spec->mode,
-                                      sha1_to_hex(anonymize ?
-                                                  anonymize_sha1(&spec->oid) :
-                                                  spec->oid.hash));
+                                      oid_to_hex(anonymize ?
+                                                 anonymize_oid(&spec->oid) :
+                                                 &spec->oid));
                        else {
                                struct object *object = lookup_object(the_repository,
                                                                      spec->oid.hash);
@@ -579,7 +602,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
                message += 2;
 
        if (commit->parents &&
-           get_object_mark(&commit->parents->item->object) != 0 &&
+           (get_object_mark(&commit->parents->item->object) != 0 ||
+            reference_excluded_commits) &&
            !full_tree) {
                parse_commit_or_die(commit->parents->item);
                diff_tree_oid(get_commit_tree_oid(commit->parents->item),
@@ -595,6 +619,13 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
                        export_blob(&diff_queued_diff.queue[i]->two->oid);
 
        refname = *revision_sources_at(&revision_sources, commit);
+       /*
+        * FIXME: string_list_remove() below for each ref is overall
+        * O(N^2).  Compared to a history walk and diffing trees, this is
+        * just lost in the noise in practice.  However, theoretically a
+        * repo may have enough refs for this to become slow.
+        */
+       string_list_remove(&extra_refs, refname, 0);
        if (anonymize) {
                refname = anonymize_refname(refname);
                anonymize_ident_line(&committer, &committer_end);
@@ -608,8 +639,10 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
                reencoded = reencode_string(message, "UTF-8", encoding);
        if (!commit->parents)
                printf("reset %s\n", refname);
-       printf("commit %s\nmark :%"PRIu32"\n%.*s\n%.*s\ndata %u\n%s",
-              refname, last_idnum,
+       printf("commit %s\nmark :%"PRIu32"\n", refname, last_idnum);
+       if (show_original_ids)
+               printf("original-oid %s\n", oid_to_hex(&commit->object.oid));
+       printf("%.*s\n%.*s\ndata %u\n%s",
               (int)(author_end - author), author,
               (int)(committer_end - committer), committer,
               (unsigned)(reencoded
@@ -620,13 +653,21 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
        unuse_commit_buffer(commit, commit_buffer);
 
        for (i = 0, p = commit->parents; p; p = p->next) {
-               int mark = get_object_mark(&p->item->object);
-               if (!mark)
+               struct object *obj = &p->item->object;
+               int mark = get_object_mark(obj);
+
+               if (!mark && !reference_excluded_commits)
                        continue;
                if (i == 0)
-                       printf("from :%d\n", mark);
+                       printf("from ");
+               else
+                       printf("merge ");
+               if (mark)
+                       printf(":%d\n", mark);
                else
-                       printf("merge :%d\n", mark);
+                       printf("%s\n", oid_to_hex(anonymize ?
+                                                 anonymize_oid(&obj->oid) :
+                                                 &obj->oid));
                i++;
        }
 
@@ -727,7 +768,7 @@ static void handle_tag(const char *name, struct tag *tag)
                                               "\n-----BEGIN PGP SIGNATURE-----\n");
                if (signature)
                        switch(signed_tag_mode) {
-                       case ABORT:
+                       case SIGNED_TAG_ABORT:
                                die("encountered signed tag %s; use "
                                    "--signed-tags=<mode> to handle it",
                                    oid_to_hex(&tag->object.oid));
@@ -752,7 +793,7 @@ static void handle_tag(const char *name, struct tag *tag)
        tagged_mark = get_object_mark(tagged);
        if (!tagged_mark) {
                switch(tag_of_filtered_mode) {
-               case ABORT:
+               case TAG_FILTERING_ABORT:
                        die("tag %s tags unexported object; use "
                            "--tag-of-filtered-object=<mode> to handle it",
                            oid_to_hex(&tag->object.oid));
@@ -766,18 +807,12 @@ static void handle_tag(const char *name, struct tag *tag)
                                    oid_to_hex(&tag->object.oid),
                                    type_name(tagged->type));
                        }
-                       p = (struct commit *)tagged;
-                       for (;;) {
-                               if (p->parents && p->parents->next)
-                                       break;
-                               if (p->object.flags & UNINTERESTING)
-                                       break;
-                               if (!(p->object.flags & TREESAME))
-                                       break;
-                               if (!p->parents)
-                                       die("can't find replacement commit for tag %s",
-                                            oid_to_hex(&tag->object.oid));
-                               p = p->parents->item;
+                       p = rewrite_commit((struct commit *)tagged);
+                       if (!p) {
+                               printf("reset %s\nfrom %s\n\n",
+                                      name, oid_to_hex(&null_oid));
+                               free(buf);
+                               return;
                        }
                        tagged_mark = get_object_mark(&p->object);
                }
@@ -785,8 +820,10 @@ static void handle_tag(const char *name, struct tag *tag)
 
        if (starts_with(name, "refs/tags/"))
                name += 10;
-       printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
-              name, tagged_mark,
+       printf("tag %s\nfrom :%d\n", name, tagged_mark);
+       if (show_original_ids)
+               printf("original-oid %s\n", oid_to_hex(&tag->object.oid));
+       printf("%.*s%sdata %d\n%.*s\n",
               (int)(tagger_end - tagger), tagger,
               tagger == tagger_end ? "" : "\n",
               (int)message_size, (int)message_size, message ? message : "");
@@ -804,7 +841,7 @@ static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
                /* handle nested tags */
                while (tag && tag->object.type == OBJ_TAG) {
                        parse_object(the_repository, &tag->object.oid);
-                       string_list_append(&extra_refs, full_name)->util = tag;
+                       string_list_append(&tag_refs, full_name)->util = tag;
                        tag = (struct tag *)tag->tagged;
                }
                if (!tag)
@@ -863,25 +900,30 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
                }
 
                /*
-                * This ref will not be updated through a commit, lets make
-                * sure it gets properly updated eventually.
+                * Make sure this ref gets properly updated eventually, whether
+                * through a commit or manually at the end.
                 */
-               if (*revision_sources_at(&revision_sources, commit) ||
-                   commit->object.flags & SHOWN)
+               if (e->item->type != OBJ_TAG)
                        string_list_append(&extra_refs, full_name)->util = commit;
+
                if (!*revision_sources_at(&revision_sources, commit))
                        *revision_sources_at(&revision_sources, commit) = full_name;
        }
+
+       string_list_sort(&extra_refs);
+       string_list_remove_duplicates(&extra_refs, 0);
 }
 
-static void handle_tags_and_duplicates(void)
+static void handle_tags_and_duplicates(struct string_list *extras)
 {
        struct commit *commit;
        int i;
 
-       for (i = extra_refs.nr - 1; i >= 0; i--) {
-               const char *name = extra_refs.items[i].string;
-               struct object *object = extra_refs.items[i].util;
+       for (i = extras->nr - 1; i >= 0; i--) {
+               const char *name = extras->items[i].string;
+               struct object *object = extras->items[i].util;
+               int mark;
+
                switch (object->type) {
                case OBJ_TAG:
                        handle_tag(name, (struct tag *)object);
@@ -890,9 +932,45 @@ static void handle_tags_and_duplicates(void)
                        if (anonymize)
                                name = anonymize_refname(name);
                        /* create refs pointing to already seen commits */
-                       commit = (struct commit *)object;
-                       printf("reset %s\nfrom :%d\n\n", name,
-                              get_object_mark(&commit->object));
+                       commit = rewrite_commit((struct commit *)object);
+                       if (!commit) {
+                               /*
+                                * Neither this object nor any of its
+                                * ancestors touch any relevant paths, so
+                                * it has been filtered to nothing.  Delete
+                                * it.
+                                */
+                               printf("reset %s\nfrom %s\n\n",
+                                      name, oid_to_hex(&null_oid));
+                               continue;
+                       }
+
+                       mark = get_object_mark(&commit->object);
+                       if (!mark) {
+                               /*
+                                * Getting here means we have a commit which
+                                * was excluded by a negative refspec (e.g.
+                                * fast-export ^master master).  If we are
+                                * referencing excluded commits, set the ref
+                                * to the exact commit.  Otherwise, the user
+                                * wants the branch exported but every commit
+                                * in its history to be deleted, which basically
+                                * just means deletion of the ref.
+                                */
+                               if (!reference_excluded_commits) {
+                                       /* delete the ref */
+                                       printf("reset %s\nfrom %s\n\n",
+                                              name, oid_to_hex(&null_oid));
+                                       continue;
+                               }
+                               /* set ref to commit using oid, not mark */
+                               printf("reset %s\nfrom %s\n\n", name,
+                                      oid_to_hex(&commit->object.oid));
+                               continue;
+                       }
+
+                       printf("reset %s\nfrom :%d\n\n", name, mark
+                              );
                        show_progress();
                        break;
                }
@@ -988,7 +1066,7 @@ static void handle_deletes(void)
                        continue;
 
                printf("reset %s\nfrom %s\n\n",
-                               refspec->dst, sha1_to_hex(null_sha1));
+                               refspec->dst, oid_to_hex(&null_oid));
        }
 }
 
@@ -1024,6 +1102,11 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"),
                             N_("Apply refspec to exported refs")),
                OPT_BOOL(0, "anonymize", &anonymize, N_("anonymize output")),
+               OPT_BOOL(0, "reference-excluded-parents",
+                        &reference_excluded_commits, N_("Reference parents which are not in fast-export stream by object id")),
+               OPT_BOOL(0, "show-original-ids", &show_original_ids,
+                           N_("Show original object ids of blobs/commits")),
+
                OPT_END()
        };
 
@@ -1080,7 +1163,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                }
        }
 
-       handle_tags_and_duplicates();
+       handle_tags_and_duplicates(&extra_refs);
+       handle_tags_and_duplicates(&tag_refs);
        handle_deletes();
 
        if (export_filename && lastimportid != last_idnum)
index 63e69a58011a4d3c774cd3c81ae9f10e3a96efe6..153a2bd282cac47ba72c101eaf0ca1b867d3f2cd 100644 (file)
@@ -55,6 +55,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        struct oid_array shallow = OID_ARRAY_INIT;
        struct string_list deepen_not = STRING_LIST_INIT_DUP;
        struct packet_reader reader;
+       enum protocol_version version;
 
        fetch_if_missing = 0;
 
@@ -217,11 +218,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 
        packet_reader_init(&reader, fd[0], NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE |
-                          PACKET_READ_GENTLE_ON_EOF);
+                          PACKET_READ_GENTLE_ON_EOF |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
 
-       switch (discover_version(&reader)) {
+       version = discover_version(&reader);
+       switch (version) {
        case protocol_v2:
-               die("support for protocol v2 not implemented yet");
+               get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL);
+               break;
        case protocol_v1:
        case protocol_v0:
                get_remote_heads(&reader, &ref, 0, NULL, &shallow);
@@ -231,7 +235,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        }
 
        ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought,
-                        &shallow, pack_lockfile_ptr, protocol_v0);
+                        &shallow, pack_lockfile_ptr, version);
        if (pack_lockfile) {
                printf("lock %s\n", pack_lockfile);
                fflush(stdout);
index e0140327aab23654c69e7388c23a07b98b8ff913..39c42106109f35f49550133580bf34b6eb60aa26 100644 (file)
@@ -629,9 +629,14 @@ static int find_and_replace(struct strbuf *haystack,
                            const char *needle,
                            const char *placeholder)
 {
-       const char *p = strstr(haystack->buf, needle);
+       const char *p = NULL;
        int plen, nlen;
 
+       nlen = strlen(needle);
+       if (ends_with(haystack->buf, needle))
+               p = haystack->buf + haystack->len - nlen;
+       else
+               p = strstr(haystack->buf, needle);
        if (!p)
                return 0;
 
@@ -639,7 +644,6 @@ static int find_and_replace(struct strbuf *haystack,
                return 0;
 
        plen = strlen(p);
-       nlen = strlen(needle);
        if (plen > nlen && p[nlen] != '/')
                return 0;
 
@@ -763,9 +767,6 @@ static int update_local_ref(struct ref *ref,
                        what = _("[new ref]");
                }
 
-               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
-                   (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref(msg, ref, 0);
                format_display(display, r ? '!' : '*', what,
                               r ? _("unable to update local ref") : NULL,
@@ -779,9 +780,6 @@ static int update_local_ref(struct ref *ref,
                strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "..");
                strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
-               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
-                   (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref("fast-forward", ref, 1);
                format_display(display, r ? '!' : ' ', quickref.buf,
                               r ? _("unable to update local ref") : NULL,
@@ -794,9 +792,6 @@ static int update_local_ref(struct ref *ref,
                strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "...");
                strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
-               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
-                   (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref("forced-update", ref, 1);
                format_display(display, r ? '!' : '+', quickref.buf,
                               r ? _("unable to update local ref") : _("forced update"),
@@ -892,6 +887,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                ref->force = rm->peer_ref->force;
                        }
 
+                       if (recurse_submodules != RECURSE_SUBMODULES_OFF)
+                               check_for_new_submodule_commits(&rm->old_oid);
 
                        if (!strcmp(rm->name, "HEAD")) {
                                kind = "";
@@ -1172,6 +1169,7 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
 static struct transport *prepare_transport(struct remote *remote, int deepen)
 {
        struct transport *transport;
+
        transport = transport_get(remote, NULL);
        transport_set_verbosity(transport, verbosity, progress);
        transport->family = family;
@@ -1191,9 +1189,13 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
        if (filter_options.choice) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
+               expand_list_objects_filter_spec(&filter_options,
+                                               &expanded_filter_spec);
                set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
-                          filter_options.filter_spec);
+                          expanded_filter_spec.buf);
                set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+               strbuf_release(&expanded_filter_spec);
        }
        if (negotiation_tip.nr) {
                if (transport->smart_options)
@@ -1478,7 +1480,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)
         */
        if (strcmp(remote->name, repository_format_partial_clone)) {
                if (filter_options.choice)
-                       die(_("--filter can only be used with the remote configured in core.partialClone"));
+                       die(_("--filter can only be used with the remote "
+                             "configured in extensions.partialclone"));
                return;
        }
 
@@ -1646,7 +1649,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                result = fetch_one(remote, argc, argv, prune_tags_ok);
        } else {
                if (filter_options.choice)
-                       die(_("--filter can only be used with the remote configured in core.partialClone"));
+                       die(_("--filter can only be used with the remote "
+                             "configured in extensions.partialclone"));
                /* TODO should this also die if we have a previous partial-clone? */
                result = fetch_multiple(&list);
        }
index 3c3e0f06e78009ee3ee2d54333dfc4740b8792a9..9c4c11d0adedc7ebd5c48f8c786c6780394f22b1 100644 (file)
@@ -51,16 +51,24 @@ static int name_objects;
 
 static const char *describe_object(struct object *obj)
 {
-       static struct strbuf buf = STRBUF_INIT;
-       char *name = name_objects ?
-               lookup_decoration(fsck_walk_options.object_names, obj) : NULL;
+       static struct strbuf bufs[] = {
+               STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
+       };
+       static int b = 0;
+       struct strbuf *buf;
+       char *name = NULL;
 
-       strbuf_reset(&buf);
-       strbuf_addstr(&buf, oid_to_hex(&obj->oid));
+       if (name_objects)
+               name = lookup_decoration(fsck_walk_options.object_names, obj);
+
+       buf = bufs + b;
+       b = (b + 1) % ARRAY_SIZE(bufs);
+       strbuf_reset(buf);
+       strbuf_addstr(buf, oid_to_hex(&obj->oid));
        if (name)
-               strbuf_addf(&buf, " (%s)", name);
+               strbuf_addf(buf, " (%s)", name);
 
-       return buf.buf;
+       return buf->buf;
 }
 
 static const char *printable_type(struct object *obj)
@@ -76,7 +84,7 @@ static const char *printable_type(struct object *obj)
 
        ret = type_name(obj->type);
        if (!ret)
-               ret = "unknown";
+               ret = _("unknown");
 
        return ret;
 }
@@ -104,25 +112,32 @@ static int fsck_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
-static void objreport(struct object *obj, const char *msg_type,
-                       const char *err)
-{
-       fprintf(stderr, "%s in %s %s: %s\n",
-               msg_type, printable_type(obj), describe_object(obj), err);
-}
-
 static int objerror(struct object *obj, const char *err)
 {
        errors_found |= ERROR_OBJECT;
-       objreport(obj, "error", err);
+       /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
+       fprintf_ln(stderr, _("error in %s %s: %s"),
+                  printable_type(obj), describe_object(obj), err);
        return -1;
 }
 
 static int fsck_error_func(struct fsck_options *o,
        struct object *obj, int type, const char *message)
 {
-       objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
-       return (type == FSCK_WARN) ? 0 : 1;
+       switch (type) {
+       case FSCK_WARN:
+               /* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
+               fprintf_ln(stderr, _("warning in %s %s: %s"),
+                          printable_type(obj), describe_object(obj), message);
+               return 0;
+       case FSCK_ERROR:
+               /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
+               fprintf_ln(stderr, _("error in %s %s: %s"),
+                          printable_type(obj), describe_object(obj), message);
+               return 1;
+       default:
+               BUG("%d (FSCK_IGNORE?) should never trigger this callback", type);
+       }
 }
 
 static struct object_array pending;
@@ -138,17 +153,18 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
         */
        if (!obj) {
                /* ... these references to parent->fld are safe here */
-               printf("broken link from %7s %s\n",
-                          printable_type(parent), describe_object(parent));
-               printf("broken link from %7s %s\n",
-                          (type == OBJ_ANY ? "unknown" : type_name(type)), "unknown");
+               printf_ln(_("broken link from %7s %s"),
+                         printable_type(parent), describe_object(parent));
+               printf_ln(_("broken link from %7s %s"),
+                         (type == OBJ_ANY ? _("unknown") : type_name(type)),
+                         _("unknown"));
                errors_found |= ERROR_REACHABLE;
                return 1;
        }
 
        if (type != OBJ_ANY && obj->type != type)
                /* ... and the reference to parent is safe here */
-               objerror(parent, "wrong object type in link");
+               objerror(parent, _("wrong object type in link"));
 
        if (obj->flags & REACHABLE)
                return 0;
@@ -164,10 +180,12 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
 
        if (!(obj->flags & HAS_OBJ)) {
                if (parent && !has_object_file(&obj->oid)) {
-                       printf("broken link from %7s %s\n",
-                                printable_type(parent), describe_object(parent));
-                       printf("              to %7s %s\n",
-                                printable_type(obj), describe_object(obj));
+                       printf_ln(_("broken link from %7s %s\n"
+                                   "              to %7s %s"),
+                                 printable_type(parent),
+                                 describe_object(parent),
+                                 printable_type(obj),
+                                 describe_object(obj));
                        errors_found |= ERROR_REACHABLE;
                }
                return 1;
@@ -231,8 +249,8 @@ static void check_reachable_object(struct object *obj)
                        return;
                if (has_object_pack(&obj->oid))
                        return; /* it is in pack - forget about it */
-               printf("missing %s %s\n", printable_type(obj),
-                       describe_object(obj));
+               printf_ln(_("missing %s %s"), printable_type(obj),
+                         describe_object(obj));
                errors_found |= ERROR_REACHABLE;
                return;
        }
@@ -257,8 +275,8 @@ static void check_unreachable_object(struct object *obj)
         * since this is something that is prunable.
         */
        if (show_unreachable) {
-               printf("unreachable %s %s\n", printable_type(obj),
-                       describe_object(obj));
+               printf_ln(_("unreachable %s %s"), printable_type(obj),
+                         describe_object(obj));
                return;
        }
 
@@ -276,8 +294,8 @@ static void check_unreachable_object(struct object *obj)
         */
        if (!(obj->flags & USED)) {
                if (show_dangling)
-                       printf("dangling %s %s\n", printable_type(obj),
-                              describe_object(obj));
+                       printf_ln(_("dangling %s %s"), printable_type(obj),
+                                 describe_object(obj));
                if (write_lost_and_found) {
                        char *filename = git_pathdup("lost-found/%s/%s",
                                obj->type == OBJ_COMMIT ? "commit" : "other",
@@ -285,18 +303,18 @@ static void check_unreachable_object(struct object *obj)
                        FILE *f;
 
                        if (safe_create_leading_directories_const(filename)) {
-                               error("Could not create lost-found");
+                               error(_("could not create lost-found"));
                                free(filename);
                                return;
                        }
                        f = xfopen(filename, "w");
                        if (obj->type == OBJ_BLOB) {
                                if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
-                                       die_errno("Could not write '%s'", filename);
+                                       die_errno(_("could not write '%s'"), filename);
                        } else
                                fprintf(f, "%s\n", describe_object(obj));
                        if (fclose(f))
-                               die_errno("Could not finish '%s'",
+                               die_errno(_("could not finish '%s'"),
                                          filename);
                        free(filename);
                }
@@ -313,7 +331,7 @@ static void check_unreachable_object(struct object *obj)
 static void check_object(struct object *obj)
 {
        if (verbose)
-               fprintf(stderr, "Checking %s\n", describe_object(obj));
+               fprintf_ln(stderr, _("Checking %s"), describe_object(obj));
 
        if (obj->flags & REACHABLE)
                check_reachable_object(obj);
@@ -331,7 +349,7 @@ static void check_connectivity(void)
        /* Look up all the requirements, warn about missing objects.. */
        max = get_max_object_index();
        if (verbose)
-               fprintf(stderr, "Checking connectivity (%d objects)\n", max);
+               fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max);
 
        for (i = 0; i < max; i++) {
                struct object *obj = get_indexed_object(i);
@@ -350,11 +368,11 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
        obj->flags |= SEEN;
 
        if (verbose)
-               fprintf(stderr, "Checking %s %s\n",
-                       printable_type(obj), describe_object(obj));
+               fprintf_ln(stderr, _("Checking %s %s"),
+                          printable_type(obj), describe_object(obj));
 
        if (fsck_walk(obj, NULL, &fsck_obj_options))
-               objerror(obj, "broken links");
+               objerror(obj, _("broken links"));
        err = fsck_object(obj, buffer, size, &fsck_obj_options);
        if (err)
                goto out;
@@ -363,17 +381,19 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
                struct commit *commit = (struct commit *) obj;
 
                if (!commit->parents && show_root)
-                       printf("root %s\n", describe_object(&commit->object));
+                       printf_ln(_("root %s"),
+                                 describe_object(&commit->object));
        }
 
        if (obj->type == OBJ_TAG) {
                struct tag *tag = (struct tag *) obj;
 
                if (show_tags && tag->tagged) {
-                       printf("tagged %s %s", printable_type(tag->tagged),
-                               describe_object(tag->tagged));
-                       printf(" (%s) in %s\n", tag->tag,
-                               describe_object(&tag->object));
+                       printf_ln(_("tagged %s %s (%s) in %s"),
+                                 printable_type(tag->tagged),
+                                 describe_object(tag->tagged),
+                                 tag->tag,
+                                 describe_object(&tag->object));
                }
        }
 
@@ -381,7 +401,8 @@ out:
        if (obj->type == OBJ_TREE)
                free_tree_buffer((struct tree *)obj);
        if (obj->type == OBJ_COMMIT)
-               free_commit_buffer((struct commit *)obj);
+               free_commit_buffer(the_repository->parsed_objects,
+                                  (struct commit *)obj);
        return err;
 }
 
@@ -397,7 +418,8 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
                                  eaten);
        if (!obj) {
                errors_found |= ERROR_OBJECT;
-               return error("%s: object corrupt or missing", oid_to_hex(oid));
+               return error(_("%s: object corrupt or missing"),
+                            oid_to_hex(oid));
        }
        obj->flags &= ~(REACHABLE | SEEN);
        obj->flags |= HAS_OBJ;
@@ -421,7 +443,8 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
                        obj->flags |= USED;
                        mark_object_reachable(obj);
                } else if (!is_promisor_object(oid)) {
-                       error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
+                       error(_("%s: invalid reflog entry %s"),
+                             refname, oid_to_hex(oid));
                        errors_found |= ERROR_REACHABLE;
                }
        }
@@ -434,8 +457,8 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid
        const char *refname = cb_data;
 
        if (verbose)
-               fprintf(stderr, "Checking reflog %s->%s\n",
-                       oid_to_hex(ooid), oid_to_hex(noid));
+               fprintf_ln(stderr, _("Checking reflog %s->%s"),
+                          oid_to_hex(ooid), oid_to_hex(noid));
 
        fsck_handle_reflog_oid(refname, ooid, 0);
        fsck_handle_reflog_oid(refname, noid, timestamp);
@@ -468,13 +491,14 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
                         default_refs++;
                         return 0;
                }
-               error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
+               error(_("%s: invalid sha1 pointer %s"),
+                     refname, oid_to_hex(oid));
                errors_found |= ERROR_REACHABLE;
                /* We'll continue with the rest despite the error.. */
                return 0;
        }
        if (obj->type != OBJ_COMMIT && is_branch(refname)) {
-               error("%s: not a commit", refname);
+               error(_("%s: not a commit"), refname);
                errors_found |= ERROR_REFS;
        }
        default_refs++;
@@ -529,7 +553,7 @@ static void get_default_heads(void)
         * "show_unreachable" flag.
         */
        if (!default_refs) {
-               fprintf(stderr, "notice: No default references\n");
+               fprintf_ln(stderr, _("notice: No default references"));
                show_unreachable = 0;
        }
 }
@@ -544,7 +568,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 
        if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
                errors_found |= ERROR_OBJECT;
-               error("%s: object corrupt or missing: %s",
+               error(_("%s: object corrupt or missing: %s"),
                      oid_to_hex(oid), path);
                return 0; /* keep checking other objects */
        }
@@ -557,7 +581,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 
        if (!obj) {
                errors_found |= ERROR_OBJECT;
-               error("%s: object could not be parsed: %s",
+               error(_("%s: object could not be parsed: %s"),
                      oid_to_hex(oid), path);
                if (!eaten)
                        free(contents);
@@ -577,7 +601,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 static int fsck_cruft(const char *basename, const char *path, void *data)
 {
        if (!starts_with(basename, "tmp_obj_"))
-               fprintf(stderr, "bad sha1 file: %s\n", path);
+               fprintf_ln(stderr, _("bad sha1 file: %s"), path);
        return 0;
 }
 
@@ -592,7 +616,7 @@ static void fsck_object_dir(const char *path)
        struct progress *progress = NULL;
 
        if (verbose)
-               fprintf(stderr, "Checking object directory\n");
+               fprintf_ln(stderr, _("Checking object directory"));
 
        if (show_progress)
                progress = start_progress(_("Checking object directories"), 256);
@@ -610,29 +634,30 @@ static int fsck_head_link(const char *head_ref_name,
        int null_is_error = 0;
 
        if (verbose)
-               fprintf(stderr, "Checking %s link\n", head_ref_name);
+               fprintf_ln(stderr, _("Checking %s link"), head_ref_name);
 
        *head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);
        if (!*head_points_at) {
                errors_found |= ERROR_REFS;
-               return error("Invalid %s", head_ref_name);
+               return error(_("invalid %s"), head_ref_name);
        }
        if (!strcmp(*head_points_at, head_ref_name))
                /* detached HEAD */
                null_is_error = 1;
        else if (!starts_with(*head_points_at, "refs/heads/")) {
                errors_found |= ERROR_REFS;
-               return error("%s points to something strange (%s)",
+               return error(_("%s points to something strange (%s)"),
                             head_ref_name, *head_points_at);
        }
        if (is_null_oid(head_oid)) {
                if (null_is_error) {
                        errors_found |= ERROR_REFS;
-                       return error("%s: detached HEAD points at nothing",
+                       return error(_("%s: detached HEAD points at nothing"),
                                     head_ref_name);
                }
-               fprintf(stderr, "notice: %s points to an unborn branch (%s)\n",
-                       head_ref_name, *head_points_at + 11);
+               fprintf_ln(stderr,
+                          _("notice: %s points to an unborn branch (%s)"),
+                          head_ref_name, *head_points_at + 11);
        }
        return 0;
 }
@@ -643,12 +668,12 @@ static int fsck_cache_tree(struct cache_tree *it)
        int err = 0;
 
        if (verbose)
-               fprintf(stderr, "Checking cache tree\n");
+               fprintf_ln(stderr, _("Checking cache tree"));
 
        if (0 <= it->entry_count) {
                struct object *obj = parse_object(the_repository, &it->oid);
                if (!obj) {
-                       error("%s: invalid sha1 pointer in cache-tree",
+                       error(_("%s: invalid sha1 pointer in cache-tree"),
                              oid_to_hex(&it->oid));
                        errors_found |= ERROR_REFS;
                        return 1;
@@ -659,7 +684,7 @@ static int fsck_cache_tree(struct cache_tree *it)
                                obj, xstrdup(":"));
                mark_object_reachable(obj);
                if (obj->type != OBJ_TREE)
-                       err |= objerror(obj, "non-tree in cache-tree");
+                       err |= objerror(obj, _("non-tree in cache-tree"));
        }
        for (i = 0; i < it->subtree_nr; i++)
                err |= fsck_cache_tree(it->down[i]->cache_tree);
@@ -715,7 +740,7 @@ static struct option fsck_opts[] = {
 int cmd_fsck(int argc, const char **argv, const char *prefix)
 {
        int i;
-       struct alternate_object_database *alt;
+       struct object_directory *odb;
 
        /* fsck knows how to handle missing promisor objects */
        fetch_if_missing = 0;
@@ -751,14 +776,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
                for_each_packed_object(mark_packed_for_connectivity, NULL, 0);
        } else {
-               struct alternate_object_database *alt_odb_list;
-
-               fsck_object_dir(get_object_directory());
-
                prepare_alt_odb(the_repository);
-               alt_odb_list = the_repository->objects->alt_odb_list;
-               for (alt = alt_odb_list; alt; alt = alt->next)
-                       fsck_object_dir(alt->path);
+               for (odb = the_repository->objects->odb; odb; odb = odb->next)
+                       fsck_object_dir(odb->path);
 
                if (check_full) {
                        struct packed_git *p;
@@ -778,7 +798,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        for (p = get_all_packs(the_repository); p;
                             p = p->next) {
                                /* verify gives error messages itself */
-                               if (verify_pack(p, fsck_obj_buffer,
+                               if (verify_pack(the_repository,
+                                               p, fsck_obj_buffer,
                                                progress, count))
                                        errors_found |= ERROR_PACK;
                                count += p->num_objects;
@@ -800,7 +821,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        if (!obj || !(obj->flags & HAS_OBJ)) {
                                if (is_promisor_object(&oid))
                                        continue;
-                               error("%s: object missing", oid_to_hex(&oid));
+                               error(_("%s: object missing"), oid_to_hex(&oid));
                                errors_found |= ERROR_OBJECT;
                                continue;
                        }
@@ -812,7 +833,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        mark_object_reachable(obj);
                        continue;
                }
-               error("invalid parameter: expected sha1, got '%s'", arg);
+               error(_("invalid parameter: expected sha1, got '%s'"), arg);
                errors_found |= ERROR_OBJECT;
        }
 
@@ -860,15 +881,13 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
                const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
 
-               commit_graph_verify.argv = verify_argv;
-               commit_graph_verify.git_cmd = 1;
-               if (run_command(&commit_graph_verify))
-                       errors_found |= ERROR_COMMIT_GRAPH;
-
                prepare_alt_odb(the_repository);
-               for (alt =  the_repository->objects->alt_odb_list; alt; alt = alt->next) {
+               for (odb = the_repository->objects->odb; odb; odb = odb->next) {
+                       child_process_init(&commit_graph_verify);
+                       commit_graph_verify.argv = verify_argv;
+                       commit_graph_verify.git_cmd = 1;
                        verify_argv[2] = "--object-dir";
-                       verify_argv[3] = alt->path;
+                       verify_argv[3] = odb->path;
                        if (run_command(&commit_graph_verify))
                                errors_found |= ERROR_COMMIT_GRAPH;
                }
@@ -878,15 +897,13 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                struct child_process midx_verify = CHILD_PROCESS_INIT;
                const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
 
-               midx_verify.argv = midx_argv;
-               midx_verify.git_cmd = 1;
-               if (run_command(&midx_verify))
-                       errors_found |= ERROR_COMMIT_GRAPH;
-
                prepare_alt_odb(the_repository);
-               for (alt =  the_repository->objects->alt_odb_list; alt; alt = alt->next) {
+               for (odb = the_repository->objects->odb; odb; odb = odb->next) {
+                       child_process_init(&midx_verify);
+                       midx_verify.argv = midx_argv;
+                       midx_verify.git_cmd = 1;
                        midx_argv[2] = "--object-dir";
-                       midx_argv[3] = alt->path;
+                       midx_argv[3] = odb->path;
                        if (run_command(&midx_verify))
                                errors_found |= ERROR_COMMIT_GRAPH;
                }
index 871a56f1c5a5804db9363b117ae3891ea832bc5f..020f725acc40f413c49f812ea0e6aac0153d097c 100644 (file)
@@ -317,7 +317,7 @@ static void add_repack_all_option(struct string_list *keep_pack)
 
 static void add_repack_incremental_option(void)
 {
-       argv_array_push(&repack, "--no-write-bitmap-index");
+       argv_array_push(&repack, "--no-write-bitmap-index");
 }
 
 static int need_to_gc(void)
@@ -659,8 +659,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
 
        report_garbage = report_pack_garbage;
        reprepare_packed_git(the_repository);
-       if (pack_garbage.nr > 0)
+       if (pack_garbage.nr > 0) {
+               close_all_packs(the_repository->objects);
                clean_pack_garbage();
+       }
 
        if (gc_write_commit_graph)
                write_commit_graph_reachable(get_object_directory(), 0,
index 71df52a333cbc756b057e93bf3d2affca8157860..dd52ea968baf1b5b971c5f59152a561744cf9b92 100644 (file)
@@ -404,7 +404,10 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
                          const struct object_id *oid,
                          const char *filename, const char *path)
 {
-       struct repository submodule;
+       struct repository subrepo;
+       const struct submodule *sub = submodule_from_path(superproject,
+                                                         &null_oid, path);
+
        int hit;
 
        /*
@@ -420,12 +423,12 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
                return 0;
        }
 
-       if (repo_submodule_init(&submodule, superproject, path)) {
+       if (repo_submodule_init(&subrepo, superproject, sub)) {
                grep_read_unlock();
                return 0;
        }
 
-       repo_read_gitmodules(&submodule);
+       repo_read_gitmodules(&subrepo);
 
        /*
         * NEEDSWORK: This adds the submodule's object directory to the list of
@@ -437,7 +440,7 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
         * store is no longer global and instead is a member of the repository
         * object.
         */
-       add_to_alternates_memory(submodule.objects->objectdir);
+       add_to_alternates_memory(subrepo.objects->odb->path);
        grep_read_unlock();
 
        if (oid) {
@@ -462,14 +465,14 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
 
                init_tree_desc(&tree, data, size);
                hit = grep_tree(opt, pathspec, &tree, &base, base.len,
-                               object->type == OBJ_COMMIT, &submodule);
+                               object->type == OBJ_COMMIT, &subrepo);
                strbuf_release(&base);
                free(data);
        } else {
-               hit = grep_cache(opt, &submodule, pathspec, 1);
+               hit = grep_cache(opt, &subrepo, pathspec, 1);
        }
 
-       repo_clear(&submodule);
+       repo_clear(&subrepo);
        return hit;
 }
 
@@ -553,7 +556,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
 
                if (match != all_entries_interesting) {
                        strbuf_addstr(&name, base->buf + tn_len);
-                       match = tree_entry_interesting(&entry, &name,
+                       match = tree_entry_interesting(repo->index,
+                                                      &entry, &name,
                                                       0, pathspec);
                        strbuf_setlen(&name, name_base_len);
 
@@ -566,7 +570,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_oid(opt, entry.oid, base->buf, tn_len,
+                       hit |= grep_oid(opt, &entry.oid, base->buf, tn_len,
                                         check_attr ? base->buf + tn_len : NULL);
                } else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
@@ -574,10 +578,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                        void *data;
                        unsigned long size;
 
-                       data = lock_and_read_oid_file(entry.oid, &type, &size);
+                       data = lock_and_read_oid_file(&entry.oid, &type, &size);
                        if (!data)
                                die(_("unable to read tree (%s)"),
-                                   oid_to_hex(entry.oid));
+                                   oid_to_hex(&entry.oid));
 
                        strbuf_addch(base, '/');
                        init_tree_desc(&sub, data, size);
@@ -585,7 +589,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                                         check_attr, repo);
                        free(data);
                } else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
-                       hit |= grep_submodule(opt, repo, pathspec, entry.oid,
+                       hit |= grep_submodule(opt, repo, pathspec, &entry.oid,
                                              base->buf, base->buf + tn_len);
                }
 
index 41faffd28db8850fb97daa52c6d481375b380504..93eff7618cf1b98e3c69a7f1f159063e9e73dbe3 100644 (file)
@@ -542,8 +542,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
         * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
         * without --bare.  Catch the error early.
         */
-       git_dir = getenv(GIT_DIR_ENVIRONMENT);
-       work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
+       git_dir = xstrdup_or_null(getenv(GIT_DIR_ENVIRONMENT));
+       work_tree = xstrdup_or_null(getenv(GIT_WORK_TREE_ENVIRONMENT));
        if ((!git_dir || is_bare_repository_cfg == 1) && work_tree)
                die(_("%s (or --work-tree=<directory>) not allowed without "
                          "specifying %s (or --git-dir=<directory>)"),
@@ -582,6 +582,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        }
 
        UNLEAK(real_git_dir);
+       UNLEAK(git_dir);
+       UNLEAK(work_tree);
 
        flags |= INIT_DB_EXIST_OK;
        return init_db(git_dir, real_git_dir, template_dir, flags);
index e8e51068bd903c01e12207637d59d80ea940bf2d..c91a59dec16f43771629715f112899e2bb2b0214 100644 (file)
@@ -203,7 +203,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
            rev->diffopt.filter || rev->diffopt.flags.follow_renames)
                rev->always_show_header = 0;
 
-       if (source) {
+       if (source || w.source) {
                init_revision_sources(&revision_sources);
                rev->sources = &revision_sources;
        }
@@ -397,7 +397,8 @@ static int cmd_log_walk(struct rev_info *rev)
                         * We may show a given commit multiple times when
                         * walking the reflogs.
                         */
-                       free_commit_buffer(commit);
+                       free_commit_buffer(the_repository->parsed_objects,
+                                          commit);
                        free_commit_list(commit->parents);
                        commit->parents = NULL;
                }
@@ -641,8 +642,9 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        name,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
-                       read_tree_recursive((struct tree *)o, "", 0, 0, &match_all,
-                                       show_tree_object, rev.diffopt.file);
+                       read_tree_recursive(the_repository, (struct tree *)o, "",
+                                           0, 0, &match_all, show_tree_object,
+                                           rev.diffopt.file);
                        rev.shown_one = 1;
                        break;
                case OBJ_COMMIT:
@@ -1939,7 +1941,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                    open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
-               free_commit_buffer(commit);
+               free_commit_buffer(the_repository->parsed_objects,
+                                  commit);
 
                /* We put one extra blank line between formatted
                 * patches and this flag is used by log-tree code
index c70a9c71588a76b2ef04689fc6cd805cee4ae0b8..cde87cbeeb8e8d7389c95c33ff60e6e5c39e10cd 100644 (file)
@@ -206,17 +206,19 @@ static void show_files(struct repository *repo, struct dir_struct *dir);
 static void show_submodule(struct repository *superproject,
                           struct dir_struct *dir, const char *path)
 {
-       struct repository submodule;
+       struct repository subrepo;
+       const struct submodule *sub = submodule_from_path(superproject,
+                                                         &null_oid, path);
 
-       if (repo_submodule_init(&submodule, superproject, path))
+       if (repo_submodule_init(&subrepo, superproject, sub))
                return;
 
-       if (repo_read_index(&submodule) < 0)
+       if (repo_read_index(&subrepo) < 0)
                die("index file corrupt");
 
-       show_files(&submodule, dir);
+       show_files(&subrepo, dir);
 
-       repo_clear(&submodule);
+       repo_clear(&subrepo);
 }
 
 static void show_ce(struct repository *repo, struct dir_struct *dir,
@@ -441,7 +443,7 @@ void overlay_tree_on_index(struct index_state *istate,
                               PATHSPEC_PREFER_CWD, prefix, matchbuf);
        } else
                memset(&pathspec, 0, sizeof(pathspec));
-       if (read_tree(tree, 1, &pathspec, istate))
+       if (read_tree(the_repository, tree, 1, &pathspec, istate))
                die("unable to read tree entries %s", tree_name);
 
        for (i = 0; i < istate->cache_nr; i++) {
index 7d581d6463dc534606d27b1998c27991a800d6f1..7cad3f24ebd084fe653061f4f5899816853fc4f0 100644 (file)
@@ -185,5 +185,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
        tree = parse_tree_indirect(&oid);
        if (!tree)
                die("not a tree object");
-       return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL);
+       return !!read_tree_recursive(the_repository, tree, "", 0, 0,
+                                    &pathspec, show_tree, NULL);
 }
index c84c6e05e922f09692b9b133cd391b850c7951df..0b07263415785e11c7fc5b1a6da40a25e969b73c 100644 (file)
@@ -26,7 +26,7 @@ int cmd_merge_ours(int argc, const char **argv, const char *prefix)
         */
        if (read_cache() < 0)
                die_errno("read_cache failed");
-       if (index_differs_from("HEAD", NULL, 0))
+       if (index_differs_from(the_repository, "HEAD", NULL, 0))
                exit(2);
        exit(0);
 }
index 9b2f707c291cf903b9151c2f3673b6fe0e0ca6d5..7545136c2a6da526ab7998106f2a28bfcbe8bd81 100644 (file)
@@ -7,16 +7,16 @@
 static const char builtin_merge_recursive_usage[] =
        "git %s <base>... -- <head> <remote> ...";
 
-static const char *better_branch_name(const char *branch)
+static char *better_branch_name(const char *branch)
 {
        static char githead_env[8 + GIT_MAX_HEXSZ + 1];
        char *name;
 
        if (strlen(branch) != the_hash_algo->hexsz)
-               return branch;
+               return xstrdup(branch);
        xsnprintf(githead_env, sizeof(githead_env), "GITHEAD_%s", branch);
        name = getenv(githead_env);
-       return name ? name : branch;
+       return xstrdup(name ? name : branch);
 }
 
 int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
@@ -26,6 +26,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        int i, failed;
        struct object_id h1, h2;
        struct merge_options o;
+       char *better1, *better2;
        struct commit *result;
 
        init_merge_options(&o);
@@ -70,13 +71,17 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        if (get_oid(o.branch2, &h2))
                die(_("could not resolve ref '%s'"), o.branch2);
 
-       o.branch1 = better_branch_name(o.branch1);
-       o.branch2 = better_branch_name(o.branch2);
+       o.branch1 = better1 = better_branch_name(o.branch1);
+       o.branch2 = better2 = better_branch_name(o.branch2);
 
        if (o.verbosity >= 3)
                printf(_("Merging %s with %s\n"), o.branch1, o.branch2);
 
        failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, &result);
+
+       free(better1);
+       free(better2);
+
        if (failed < 0)
                return 128; /* die() error code */
        return failed;
index 70f6fc916765c5a5ca67c2dfe72e0480128182e3..5541ad091eb8c4b975d25be6386ea79be4831e5b 100644 (file)
@@ -154,15 +154,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->oid &&
-               b->oid &&
-               oideq(a->oid, b->oid) &&
+       return  !is_null_oid(&a->oid) &&
+               !is_null_oid(&b->oid) &&
+               oideq(&a->oid, &b->oid) &&
                a->mode == b->mode;
 }
 
 static int both_empty(struct name_entry *a, struct name_entry *b)
 {
-       return !(a->oid || b->oid);
+       return is_null_oid(&a->oid) && is_null_oid(&b->oid);
 }
 
 static struct merge_list *create_entry(unsigned stage, unsigned mode, const struct object_id *oid, const char *path)
@@ -178,7 +178,7 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const stru
 
 static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
 {
-       char *path = xmallocz(traverse_path_len(info, n));
+       char *path = xmallocz(traverse_path_len(info, n) + the_hash_algo->rawsz);
        return make_traverse_path(path, info, n);
 }
 
@@ -192,8 +192,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->oid, path);
-       final = create_entry(0, result->mode, result->oid, path);
+       orig = create_entry(2, ours->mode, &ours->oid, path);
+       final = create_entry(0, result->mode, &result->oid, path);
 
        final->link = orig;
 
@@ -217,7 +217,7 @@ static void unresolved_directory(const struct traverse_info *info,
 
        newbase = traverse_path(info, p);
 
-#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid : NULL)
+#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)
        buf0 = fill_tree_descriptor(t + 0, ENTRY_OID(n + 0));
        buf1 = fill_tree_descriptor(t + 1, ENTRY_OID(n + 1));
        buf2 = fill_tree_descriptor(t + 2, ENTRY_OID(n + 2));
@@ -243,7 +243,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->oid, path);
+       link = create_entry(stage, n->mode, &n->oid, path);
        link->link = entry;
        return link;
 }
@@ -318,7 +318,7 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
        }
 
        if (same_entry(entry+0, entry+1)) {
-               if (entry[2].oid && !S_ISDIR(entry[2].mode)) {
+               if (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) {
                        /* We did not touch, they modified -- take theirs */
                        resolve(info, entry+1, entry+2);
                        return mask;
@@ -346,7 +346,7 @@ static void merge_trees(struct tree_desc t[3], const char *base)
 
        setup_traverse_info(&info, base);
        info.fn = threeway_callback;
-       traverse_trees(3, t, &info);
+       traverse_trees(&the_index, 3, t, &info);
 }
 
 static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
index c3c976d471ce78268e673a5135a4c37ebc6d0f6d..dc0b7cc521c26f4c0ef1c6440d17c0f35ac1c983 100644 (file)
@@ -128,7 +128,7 @@ static int option_read_message(struct parse_opt_ctx_t *ctx,
                ctx->argc--;
                arg = *++ctx->argv;
        } else
-               return opterror(opt, "requires a value", 0);
+               return error(_("option `%s' requires a value"), opt->long_name);
 
        if (buf->len)
                strbuf_addch(buf, '\n');
@@ -578,7 +578,7 @@ static void parse_branch_merge_options(char *bmo)
        argc = split_cmdline(bmo, &argv);
        if (argc < 0)
                die(_("Bad branch.%s.mergeoptions string: %s"), branch,
-                   split_cmdline_strerror(argc));
+                   _(split_cmdline_strerror(argc)));
        REALLOC_ARRAY(argv, argc + 2);
        MOVE_ARRAY(argv + 1, argv, argc + 1);
        argc++;
@@ -897,7 +897,7 @@ static int suggest_conflicts(void)
        filename = git_path_merge_msg(the_repository);
        fp = xfopen(filename, "a");
 
-       append_conflicts_hint(&msgbuf);
+       append_conflicts_hint(&the_index, &msgbuf);
        fputs(msgbuf.buf, fp);
        strbuf_release(&msgbuf);
        fclose(fp);
index c78b7a0c5b7fbea81320ae9873553c437d82e73e..4996a670f71333de09e1a0311fde85d6985c9c91 100644 (file)
@@ -813,7 +813,7 @@ static int merge(int argc, const char **argv, const char *prefix)
                usage_with_options(git_notes_merge_usage, options);
        }
 
-       init_notes_merge_options(&o);
+       init_notes_merge_options(the_repository, &o);
        o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT;
 
        if (do_abort)
index 411aefd6875b2d35ee4a12d1a043ba50027021b3..8eb5391c089d1add157cb677db037e30cd9ada16 100644 (file)
@@ -1334,7 +1334,7 @@ static void add_pbase_object(struct tree_desc *tree,
                if (cmp < 0)
                        return;
                if (name[cmplen] != '/') {
-                       add_object_entry(entry.oid,
+                       add_object_entry(&entry.oid,
                                         object_type(entry.mode),
                                         fullname, 1);
                        return;
@@ -1345,7 +1345,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.oid);
+                       tree = pbase_tree_get(&entry.oid);
                        if (!tree)
                                return;
                        init_tree_desc(&sub, tree->tree_data, tree->tree_size);
@@ -1953,11 +1953,6 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
        return 0;
 }
 
-/* Protect access to object database */
-static pthread_mutex_t read_mutex;
-#define read_lock()            pthread_mutex_lock(&read_mutex)
-#define read_unlock()          pthread_mutex_unlock(&read_mutex)
-
 /* Protect delta_cache_size */
 static pthread_mutex_t cache_mutex;
 #define cache_lock()           pthread_mutex_lock(&cache_mutex)
@@ -1993,11 +1988,11 @@ unsigned long oe_get_size_slow(struct packing_data *pack,
        unsigned long used, avail, size;
 
        if (e->type_ != OBJ_OFS_DELTA && e->type_ != OBJ_REF_DELTA) {
-               read_lock();
+               packing_data_lock(&to_pack);
                if (oid_object_info(the_repository, &e->idx.oid, &size) < 0)
                        die(_("unable to get size of %s"),
                            oid_to_hex(&e->idx.oid));
-               read_unlock();
+               packing_data_unlock(&to_pack);
                return size;
        }
 
@@ -2005,7 +2000,7 @@ unsigned long oe_get_size_slow(struct packing_data *pack,
        if (!p)
                BUG("when e->type is a delta, it must belong to a pack");
 
-       read_lock();
+       packing_data_lock(&to_pack);
        w_curs = NULL;
        buf = use_pack(p, &w_curs, e->in_pack_offset, &avail);
        used = unpack_object_header_buffer(buf, avail, &type, &size);
@@ -2014,7 +2009,7 @@ unsigned long oe_get_size_slow(struct packing_data *pack,
                    oid_to_hex(&e->idx.oid));
 
        unuse_pack(&w_curs);
-       read_unlock();
+       packing_data_unlock(&to_pack);
        return size;
 }
 
@@ -2076,9 +2071,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
 
        /* Load data if not already done */
        if (!trg->data) {
-               read_lock();
+               packing_data_lock(&to_pack);
                trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz);
-               read_unlock();
+               packing_data_unlock(&to_pack);
                if (!trg->data)
                        die(_("object %s cannot be read"),
                            oid_to_hex(&trg_entry->idx.oid));
@@ -2089,9 +2084,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
                *mem_usage += sz;
        }
        if (!src->data) {
-               read_lock();
+               packing_data_lock(&to_pack);
                src->data = read_object_file(&src_entry->idx.oid, &type, &sz);
-               read_unlock();
+               packing_data_unlock(&to_pack);
                if (!src->data) {
                        if (src_entry->preferred_base) {
                                static int warned = 0;
@@ -2337,9 +2332,9 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
 
 static void try_to_free_from_threads(size_t size)
 {
-       read_lock();
+       packing_data_lock(&to_pack);
        release_pack_memory(size);
-       read_unlock();
+       packing_data_unlock(&to_pack);
 }
 
 static try_to_free_t old_try_to_free_routine;
@@ -2381,7 +2376,6 @@ static pthread_cond_t progress_cond;
  */
 static void init_threaded_search(void)
 {
-       init_recursive_mutex(&read_mutex);
        pthread_mutex_init(&cache_mutex, NULL);
        pthread_mutex_init(&progress_mutex, NULL);
        pthread_cond_init(&progress_cond, NULL);
@@ -2392,7 +2386,6 @@ static void cleanup_threaded_search(void)
 {
        set_try_to_free_routine(old_try_to_free_routine);
        pthread_cond_destroy(&progress_cond);
-       pthread_mutex_destroy(&read_mutex);
        pthread_mutex_destroy(&cache_mutex);
        pthread_mutex_destroy(&progress_mutex);
 }
@@ -2610,7 +2603,7 @@ static void prepare_pack(int window, int depth)
        unsigned n;
 
        if (use_delta_islands)
-               resolve_tree_islands(progress, &to_pack);
+               resolve_tree_islands(the_repository, progress, &to_pack);
 
        get_object_details();
 
@@ -3084,14 +3077,16 @@ static void record_recent_commit(struct commit *commit, void *data)
 static void get_object_list(int ac, const char **av)
 {
        struct rev_info revs;
+       struct setup_revision_opt s_r_opt = {
+               .allow_exclude_promisor_objects = 1,
+       };
        char line[1000];
        int flags = 0;
        int save_warning;
 
        repo_init_revisions(the_repository, &revs, NULL);
        save_commit_buffer = 0;
-       revs.allow_exclude_promisor_objects_opt = 1;
-       setup_revisions(ac, av, &revs, NULL);
+       setup_revisions(ac, av, &revs, &s_r_opt);
 
        /* make sure shallows are read */
        is_repository_shallow(the_repository);
@@ -3131,7 +3126,7 @@ static void get_object_list(int ac, const char **av)
                return;
 
        if (use_delta_islands)
-               load_delta_islands();
+               load_delta_islands(the_repository);
 
        if (prepare_revision_walk(&revs))
                die(_("revision walk setup failed"));
@@ -3470,7 +3465,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                }
        }
 
-       prepare_packing_data(&to_pack);
+       prepare_packing_data(the_repository, &to_pack);
 
        if (progress)
                progress_state = start_progress(_("Enumerating objects"), 0);
index e42653b99cffe2f42f2fb853999a41465878ce02..1ec9ddd751df6644d2c39ace41a1494800082638 100644 (file)
@@ -120,7 +120,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        save_commit_buffer = 0;
        read_replace_refs = 0;
        ref_paranoia = 1;
-       revs.allow_exclude_promisor_objects_opt = 1;
        repo_init_revisions(the_repository, &revs, prefix);
 
        argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
index 1b90622b1311187612ef8b33995c92a6802eb343..74808b94554a78e720128a721f21dc0d20dd839e 100644 (file)
@@ -899,7 +899,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
                        die(_("Updating an unborn branch with changes added to the index."));
 
                if (!autostash)
-                       require_clean_work_tree(N_("pull with rebase"),
+                       require_clean_work_tree(the_repository,
+                               N_("pull with rebase"),
                                _("please commit or stash them."), 1, 0);
 
                if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
index 8bb8a0849ba90aff5d295acc3894b2034cbbdef2..021dd3b1e48979086de68a434d4eddbbeb074df3 100644 (file)
@@ -143,7 +143,9 @@ static int push_url_of_remote(struct remote *remote, const char ***url_p)
        return remote->url_nr;
 }
 
-static NORETURN int die_push_simple(struct branch *branch, struct remote *remote) {
+static NORETURN int die_push_simple(struct branch *branch,
+                                   struct remote *remote)
+{
        /*
         * There's no point in using shorten_unambiguous_ref here,
         * as the ambiguity would be on the remote side, not what
@@ -355,7 +357,8 @@ static int push_with_options(struct transport *transport, struct refspec *rs,
 
        if (verbosity > 0)
                fprintf(stderr, _("Pushing to %s\n"), transport->url);
-       err = transport_push(transport, rs, flags, &reject_reasons);
+       err = transport_push(the_repository, transport,
+                            rs, flags, &reject_reasons);
        if (err != 0) {
                fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR));
                error(_("failed to push some refs to '%s'"), transport->url);
index 183ee8c1e5d4de53d10e9ed00c624f615baf3fc9..ac255ad2c243192a9afd3b4578a2a501eb250aff 100644 (file)
@@ -258,7 +258,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
         * what came from the tree.
         */
        if (nr_trees == 1 && !opts.prefix)
-               prime_cache_tree(&the_index, trees[0]);
+               prime_cache_tree(the_repository,
+                                the_repository->index,
+                                trees[0]);
 
        if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die("unable to write new index file");
index a2ab68ed063224be123a3f5e8831afdc3ed649a0..c2c2d51a3b03edf17de5b37811dadd66ca4f774b 100644 (file)
@@ -105,7 +105,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,
        if (restrict_revision)
                argv_array_push(&make_script_args, restrict_revision);
 
-       ret = sequencer_make_script(todo_list,
+       ret = sequencer_make_script(the_repository, todo_list,
                                    make_script_args.argc, make_script_args.argv,
                                    flags);
        fclose(todo_list);
@@ -114,7 +114,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,
                error(_("could not generate todo list"));
        else {
                discard_cache();
-               ret = complete_action(opts, flags, shortrevisions, onto_name, onto,
+               ret = complete_action(the_repository, opts, flags,
+                                     shortrevisions, onto_name, onto,
                                      head_hash, cmd, autosquash);
        }
 
@@ -192,6 +193,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
                OPT_STRING(0, "onto-name", &onto_name, N_("onto-name"), N_("onto name")),
                OPT_STRING(0, "cmd", &cmd, N_("cmd"), N_("the command to run")),
                OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_auto),
+               OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
+                        N_("automatically re-schedule any `exec` that fails")),
                OPT_END()
        };
 
@@ -232,14 +235,14 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
        case SKIP: {
                struct string_list merge_rr = STRING_LIST_INIT_DUP;
 
-               rerere_clear(&merge_rr);
+               rerere_clear(the_repository, &merge_rr);
                /* fallthrough */
        case CONTINUE:
-               ret = sequencer_continue(&opts);
+               ret = sequencer_continue(the_repository, &opts);
                break;
        }
        case EDIT_TODO:
-               ret = edit_todo_list(flags);
+               ret = edit_todo_list(the_repository, flags);
                break;
        case SHOW_CURRENT_PATCH: {
                struct child_process cmd = CHILD_PROCESS_INIT;
@@ -252,16 +255,16 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
        }
        case SHORTEN_OIDS:
        case EXPAND_OIDS:
-               ret = transform_todos(flags);
+               ret = transform_todos(the_repository, flags);
                break;
        case CHECK_TODO_LIST:
-               ret = check_todo_list();
+               ret = check_todo_list(the_repository);
                break;
        case REARRANGE_SQUASH:
-               ret = rearrange_squash();
+               ret = rearrange_squash(the_repository);
                break;
        case ADD_EXEC:
-               ret = sequencer_add_exec_commands(cmd);
+               ret = sequencer_add_exec_commands(the_repository, cmd);
                break;
        default:
                BUG("invalid command '%d'", command);
index b5c99ec10c200a5fccb293c0acadae80642f2808..3f9b26f7295e4b66f8a59d72ed2d00ea7900f980 100644 (file)
@@ -104,6 +104,7 @@ struct rebase_options {
        int rebase_merges, rebase_cousins;
        char *strategy, *strategy_opts;
        struct strbuf git_format_patch_opt;
+       int reschedule_failed_exec;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -122,7 +123,7 @@ static void imply_interactive(struct rebase_options *opts, const char *option)
        case REBASE_PRESERVE_MERGES:
                break;
        case REBASE_MERGE:
-               /* we silently *upgrade* --merge to --interactive if needed */
+               /* we now implement --merge via --interactive */
        default:
                opts->type = REBASE_INTERACTIVE; /* implied */
                break;
@@ -185,10 +186,7 @@ static int read_basic_state(struct rebase_options *opts)
        if (get_oid(buf.buf, &opts->orig_head))
                return error(_("invalid orig-head: '%s'"), buf.buf);
 
-       strbuf_reset(&buf);
-       if (read_one(state_dir_path("quiet", opts), &buf))
-               return -1;
-       if (buf.len)
+       if (file_exists(state_dir_path("quiet", opts)))
                opts->flags &= ~REBASE_NO_QUIET;
        else
                opts->flags |= REBASE_NO_QUIET;
@@ -354,7 +352,8 @@ static int run_specific_rebase(struct rebase_options *opts)
                argv_array_pushf(&child.env_array, "GIT_CHERRY_PICK_HELP=%s",
                                 resolvemsg);
                if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
-                       argv_array_push(&child.env_array, "GIT_EDITOR=:");
+                       argv_array_push(&child.env_array,
+                                       "GIT_SEQUENCE_EDITOR=:");
                        opts->autosquash = 0;
                }
 
@@ -415,6 +414,8 @@ static int run_specific_rebase(struct rebase_options *opts)
                        argv_array_push(&child.args, opts->gpg_sign_opt);
                if (opts->signoff)
                        argv_array_push(&child.args, "--signoff");
+               if (opts->reschedule_failed_exec)
+                       argv_array_push(&child.args, "--reschedule-failed-exec");
 
                status = run_command(&child);
                goto finished_rebase;
@@ -475,7 +476,7 @@ static int run_specific_rebase(struct rebase_options *opts)
        if (is_interactive(opts) &&
            !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
                strbuf_addstr(&script_snippet,
-                             "GIT_EDITOR=:; export GIT_EDITOR; ");
+                             "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
                opts->autosquash = 0;
        }
 
@@ -484,10 +485,6 @@ static int run_specific_rebase(struct rebase_options *opts)
                backend = "git-rebase--am";
                backend_func = "git_rebase__am";
                break;
-       case REBASE_MERGE:
-               backend = "git-rebase--merge";
-               backend_func = "git_rebase__merge";
-               break;
        case REBASE_PRESERVE_MERGES:
                backend = "git-rebase--preserve-merges";
                backend_func = "git_rebase__preserve_merges";
@@ -530,6 +527,7 @@ finished_rebase:
 
 #define RESET_HEAD_DETACH (1<<0)
 #define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
 
 static int reset_head(struct object_id *oid, const char *action,
                      const char *switch_to_branch, unsigned flags,
@@ -537,6 +535,7 @@ static int reset_head(struct object_id *oid, const char *action,
 {
        unsigned detach_head = flags & RESET_HEAD_DETACH;
        unsigned reset_hard = flags & RESET_HEAD_HARD;
+       unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
        struct object_id head_oid;
        struct tree_desc desc[2] = { { NULL }, { NULL } };
        struct lock_file lock = LOCK_INIT;
@@ -598,7 +597,7 @@ static int reset_head(struct object_id *oid, const char *action,
        }
 
        tree = parse_tree_indirect(oid);
-       prime_cache_tree(the_repository->index, tree);
+       prime_cache_tree(the_repository, the_repository->index, tree);
 
        if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
                ret = error(_("could not write index"));
@@ -636,6 +635,10 @@ static int reset_head(struct object_id *oid, const char *action,
                        ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
                                         UPDATE_REFS_MSG_ON_ERR);
        }
+       if (run_hook)
+               run_hook_le(NULL, "post-checkout",
+                           oid_to_hex(orig ? orig : &null_oid),
+                           oid_to_hex(oid), "1", NULL);
 
 leave_reset_head:
        strbuf_release(&msg);
@@ -674,6 +677,11 @@ static int rebase_config(const char *var, const char *value, void *data)
                return 0;
        }
 
+       if (!strcmp(var, "rebase.reschedulefailedexec")) {
+               opts->reschedule_failed_exec = git_config_bool(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, data);
 }
 
@@ -746,6 +754,23 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
        return 0;
 }
 
+struct opt_y {
+       struct string_list *list;
+       struct rebase_options *options;
+};
+
+static int parse_opt_y(const struct option *opt, const char *arg, int unset)
+{
+       struct opt_y *o = opt->value;
+
+       if (unset || !arg)
+               return -1;
+
+       o->options->reschedule_failed_exec = 1;
+       string_list_append(o->list, arg);
+       return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
        struct branch *current_branch = branch_get(NULL);
@@ -826,6 +851,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        struct string_list strategy_options = STRING_LIST_INIT_NODUP;
        struct object_id squash_onto;
        char *squash_onto_name = NULL;
+       struct opt_y opt_y = { .list = &exec, .options = &options };
        struct option builtin_rebase_options[] = {
                OPT_STRING(0, "onto", &options.onto_name,
                           N_("revision"),
@@ -903,6 +929,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
                                N_("add exec lines after each commit of the "
                                   "editable list")),
+               { OPTION_CALLBACK, 'y', NULL, &opt_y, N_("<cmd>"),
+                       N_("same as --reschedule-failed-exec -x <cmd>"),
+                       PARSE_OPT_NONEG, parse_opt_y },
                OPT_BOOL(0, "allow-empty-message",
                         &options.allow_empty_message,
                         N_("allow rebasing commits with empty messages")),
@@ -920,6 +949,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                   "strategy")),
                OPT_BOOL(0, "root", &options.root,
                         N_("rebase all reachable commits up to the root(s)")),
+               OPT_BOOL(0, "reschedule-failed-exec",
+                        &options.reschedule_failed_exec,
+                        N_("automatically re-schedule any `exec` that fails")),
                OPT_END(),
        };
        int i;
@@ -1024,7 +1056,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                             &lock_file);
                rollback_lock_file(&lock_file);
 
-               if (has_unstaged_changes(1)) {
+               if (has_unstaged_changes(the_repository, 1)) {
                        puts(_("You must edit all merge conflicts and then\n"
                               "mark them as resolved using git add"));
                        exit(1);
@@ -1039,13 +1071,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                options.action = "skip";
                set_reflog_action(&options);
 
-               rerere_clear(&merge_rr);
+               rerere_clear(the_repository, &merge_rr);
                string_list_clear(&merge_rr, 1);
 
                if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
                               NULL, NULL) < 0)
                        die(_("could not discard worktree changes"));
-               remove_branch_state();
+               remove_branch_state(the_repository);
                if (read_basic_state(&options))
                        exit(1);
                goto run_rebase;
@@ -1055,7 +1087,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                options.action = "abort";
                set_reflog_action(&options);
 
-               rerere_clear(&merge_rr);
+               rerere_clear(the_repository, &merge_rr);
                string_list_clear(&merge_rr, 1);
 
                if (read_basic_state(&options))
@@ -1065,7 +1097,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                               NULL, NULL) < 0)
                        die(_("could not move back to %s"),
                            oid_to_hex(&options.orig_head));
-               remove_branch_state();
+               remove_branch_state(the_repository);
                ret = finish_rebase(&options);
                goto cleanup;
        }
@@ -1194,6 +1226,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (options.type == REBASE_MERGE)
+               imply_interactive(&options, "--merge");
+
        if (options.root && !options.onto_name)
                imply_interactive(&options, "--root without --onto");
 
@@ -1216,6 +1251,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                break;
        }
 
+       if (options.reschedule_failed_exec && !is_interactive(&options))
+               die(_("--reschedule-failed-exec requires an interactive rebase"));
+
        if (options.git_am_opts.argc) {
                /* all am options except -q are compatible only with --am */
                for (i = options.git_am_opts.argc - 1; i >= 0; i--)
@@ -1223,14 +1261,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                break;
 
                if (is_interactive(&options) && i >= 0)
-                       die(_("error: cannot combine interactive options "
-                             "(--interactive, --exec, --rebase-merges, "
-                             "--preserve-merges, --keep-empty, --root + "
-                             "--onto) with am options (%s)"), buf.buf);
-               if (options.type == REBASE_MERGE && i >= 0)
-                       die(_("error: cannot combine merge options (--merge, "
-                             "--strategy, --strategy-option) with am options "
-                             "(%s)"), buf.buf);
+                       die(_("cannot combine am options with either "
+                             "interactive or merge options"));
        }
 
        if (options.signoff) {
@@ -1241,22 +1273,27 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                options.flags |= REBASE_FORCE;
        }
 
-       if (options.type == REBASE_PRESERVE_MERGES)
+       if (options.type == REBASE_PRESERVE_MERGES) {
                /*
                 * Note: incompatibility with --signoff handled in signoff block above
                 * Note: incompatibility with --interactive is just a strong warning;
                 *       git-rebase.txt caveats with "unless you know what you are doing"
                 */
                if (options.rebase_merges)
-                       die(_("error: cannot combine '--preserve-merges' with "
+                       die(_("cannot combine '--preserve-merges' with "
                              "'--rebase-merges'"));
 
+               if (options.reschedule_failed_exec)
+                       die(_("error: cannot combine '--preserve-merges' with "
+                             "'--reschedule-failed-exec'"));
+       }
+
        if (options.rebase_merges) {
                if (strategy_options.nr)
-                       die(_("error: cannot combine '--rebase-merges' with "
+                       die(_("cannot combine '--rebase-merges' with "
                              "'--strategy-option'"));
                if (options.strategy)
-                       die(_("error: cannot combine '--rebase-merges' with "
+                       die(_("cannot combine '--rebase-merges' with "
                              "'--strategy'"));
        }
 
@@ -1381,7 +1418,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        update_index_if_able(&the_index, &lock_file);
                rollback_lock_file(&lock_file);
 
-               if (has_unstaged_changes(1) || has_uncommitted_changes(1)) {
+               if (has_unstaged_changes(the_repository, 1) ||
+                   has_uncommitted_changes(the_repository, 1)) {
                        const char *autostash =
                                state_dir_path("autostash", &options);
                        struct child_process stash = CHILD_PROCESS_INIT;
@@ -1427,7 +1465,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                }
        }
 
-       if (require_clean_work_tree("rebase",
+       if (require_clean_work_tree(the_repository, "rebase",
                                    _("Please commit or stash them."), 1, 1)) {
                ret = 1;
                goto cleanup;
@@ -1465,7 +1503,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                            getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
                                            options.switch_to);
                                if (reset_head(&oid, "checkout",
-                                              options.head_name, 0,
+                                              options.head_name,
+                                              RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
                                               NULL, buf.buf) < 0) {
                                        ret = !!error(_("could not switch to "
                                                        "%s"),
@@ -1539,7 +1578,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        strbuf_addf(&msg, "%s: checkout %s",
                    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
        if (reset_head(&options.onto->object.oid, "checkout", NULL,
-                      RESET_HEAD_DETACH, NULL, msg.buf))
+                      RESET_HEAD_DETACH | RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
+                      NULL, msg.buf))
                die(_("Could not detach HEAD"));
        strbuf_release(&msg);
 
index 33187bd8e90252c283b7154bc7026e01d4e754ef..d58b7750b6b46565ca4ebb8299e5260943aa364e 100644 (file)
@@ -1569,30 +1569,29 @@ static void queue_commands_from_cert(struct command **tail,
        }
 }
 
-static struct command *read_head_info(struct oid_array *shallow)
+static struct command *read_head_info(struct packet_reader *reader,
+                                     struct oid_array *shallow)
 {
        struct command *commands = NULL;
        struct command **p = &commands;
        for (;;) {
-               char *line;
-               int len, linelen;
+               int linelen;
 
-               line = packet_read_line(0, &len);
-               if (!line)
+               if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                        break;
 
-               if (len > 8 && starts_with(line, "shallow ")) {
+               if (reader->pktlen > 8 && starts_with(reader->line, "shallow ")) {
                        struct object_id oid;
-                       if (get_oid_hex(line + 8, &oid))
+                       if (get_oid_hex(reader->line + 8, &oid))
                                die("protocol error: expected shallow sha, got '%s'",
-                                   line + 8);
+                                   reader->line + 8);
                        oid_array_append(shallow, &oid);
                        continue;
                }
 
-               linelen = strlen(line);
-               if (linelen < len) {
-                       const char *feature_list = line + linelen + 1;
+               linelen = strlen(reader->line);
+               if (linelen < reader->pktlen) {
+                       const char *feature_list = reader->line + linelen + 1;
                        if (parse_feature_request(feature_list, "report-status"))
                                report_status = 1;
                        if (parse_feature_request(feature_list, "side-band-64k"))
@@ -1607,28 +1606,32 @@ static struct command *read_head_info(struct oid_array *shallow)
                                use_push_options = 1;
                }
 
-               if (!strcmp(line, "push-cert")) {
+               if (!strcmp(reader->line, "push-cert")) {
                        int true_flush = 0;
-                       char certbuf[1024];
+                       int saved_options = reader->options;
+                       reader->options &= ~PACKET_READ_CHOMP_NEWLINE;
 
                        for (;;) {
-                               len = packet_read(0, NULL, NULL,
-                                                 certbuf, sizeof(certbuf), 0);
-                               if (!len) {
+                               packet_reader_read(reader);
+                               if (reader->status == PACKET_READ_FLUSH) {
                                        true_flush = 1;
                                        break;
                                }
-                               if (!strcmp(certbuf, "push-cert-end\n"))
+                               if (reader->status != PACKET_READ_NORMAL) {
+                                       die("protocol error: got an unexpected packet");
+                               }
+                               if (!strcmp(reader->line, "push-cert-end\n"))
                                        break; /* end of cert */
-                               strbuf_addstr(&push_cert, certbuf);
+                               strbuf_addstr(&push_cert, reader->line);
                        }
+                       reader->options = saved_options;
 
                        if (true_flush)
                                break;
                        continue;
                }
 
-               p = queue_command(p, line, linelen);
+               p = queue_command(p, reader->line, linelen);
        }
 
        if (push_cert.len)
@@ -1637,18 +1640,14 @@ static struct command *read_head_info(struct oid_array *shallow)
        return commands;
 }
 
-static void read_push_options(struct string_list *options)
+static void read_push_options(struct packet_reader *reader,
+                             struct string_list *options)
 {
        while (1) {
-               char *line;
-               int len;
-
-               line = packet_read_line(0, &len);
-
-               if (!line)
+               if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                        break;
 
-               string_list_append(options, line);
+               string_list_append(options, reader->line);
        }
 }
 
@@ -1924,6 +1923,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        struct oid_array shallow = OID_ARRAY_INIT;
        struct oid_array ref = OID_ARRAY_INIT;
        struct shallow_info si;
+       struct packet_reader reader;
 
        struct option options[] = {
                OPT__QUIET(&quiet, N_("quiet")),
@@ -1986,12 +1986,16 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        if (advertise_refs)
                return 0;
 
-       if ((commands = read_head_info(&shallow)) != NULL) {
+       packet_reader_init(&reader, 0, NULL, 0,
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
+
+       if ((commands = read_head_info(&reader, &shallow)) != NULL) {
                const char *unpack_status = NULL;
                struct string_list push_options = STRING_LIST_INIT_DUP;
 
                if (use_push_options)
-                       read_push_options(&push_options);
+                       read_push_options(&reader, &push_options);
                if (!check_cert_push_options(&push_options)) {
                        struct command *cmd;
                        for (cmd = commands; cmd; cmd = cmd->next)
index 7a85e4b1640b12d4c8f7958cb59b66e037fd2ce2..1f1010e2d9aec2b95487007ec30509fcbda605d6 100644 (file)
 
 /* NEEDSWORK: switch to using parse_options */
 static const char reflog_expire_usage[] =
-"git reflog expire [--expire=<time>] [--expire-unreachable=<time>] [--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] [--verbose] [--all] <refs>...";
+N_("git reflog expire [--expire=<time>] "
+   "[--expire-unreachable=<time>] "
+   "[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] "
+   "[--verbose] [--all] <refs>...");
 static const char reflog_delete_usage[] =
-"git reflog delete [--rewrite] [--updateref] [--dry-run | -n] [--verbose] <refs>...";
+N_("git reflog delete [--rewrite] [--updateref] "
+   "[--dry-run | -n] [--verbose] <refs>...");
 static const char reflog_exists_usage[] =
-"git reflog exists <ref>";
+N_("git reflog exists <ref>");
 
 static timestamp_t default_reflog_expire;
 static timestamp_t default_reflog_expire_unreachable;
@@ -90,8 +94,8 @@ static int tree_is_complete(const struct object_id *oid)
        init_tree_desc(&desc, tree->buffer, tree->size);
        complete = 1;
        while (tree_entry(&desc, &entry)) {
-               if (!has_sha1_file(entry.oid->hash) ||
-                   (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid))) {
+               if (!has_sha1_file(entry.oid.hash) ||
+                   (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
                        tree->object.flags |= INCOMPLETE;
                        complete = 0;
                }
@@ -585,7 +589,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                        break;
                }
                else if (arg[0] == '-')
-                       usage(reflog_expire_usage);
+                       usage(_(reflog_expire_usage));
                else
                        break;
        }
@@ -598,7 +602,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
        if (cb.cmd.stalefix) {
                repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
                if (flags & EXPIRE_REFLOGS_VERBOSE)
-                       printf("Marking reachable objects...");
+                       printf(_("Marking reachable objects..."));
                mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
                if (flags & EXPIRE_REFLOGS_VERBOSE)
                        putchar('\n');
@@ -636,7 +640,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                char *ref;
                struct object_id oid;
                if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) {
-                       status |= error("%s points nowhere!", argv[i]);
+                       status |= error(_("%s points nowhere!"), argv[i]);
                        continue;
                }
                set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
@@ -682,13 +686,13 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
                        break;
                }
                else if (arg[0] == '-')
-                       usage(reflog_delete_usage);
+                       usage(_(reflog_delete_usage));
                else
                        break;
        }
 
        if (argc - i < 1)
-               return error("Nothing to delete?");
+               return error(_("no reflog specified to delete"));
 
        for ( ; i < argc; i++) {
                const char *spec = strstr(argv[i], "@{");
@@ -697,12 +701,12 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
                int recno;
 
                if (!spec) {
-                       status |= error("Not a reflog: %s", argv[i]);
+                       status |= error(_("not a reflog: %s"), argv[i]);
                        continue;
                }
 
                if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) {
-                       status |= error("no reflog for '%s'", argv[i]);
+                       status |= error(_("no reflog for '%s'"), argv[i]);
                        continue;
                }
 
@@ -737,7 +741,7 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
                        break;
                }
                else if (arg[0] == '-')
-                       usage(reflog_exists_usage);
+                       usage(_(reflog_exists_usage));
                else
                        break;
        }
@@ -745,10 +749,10 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
        start = i;
 
        if (argc - start != 1)
-               usage(reflog_exists_usage);
+               usage(_(reflog_exists_usage));
 
        if (check_refname_format(argv[start], REFNAME_ALLOW_ONELEVEL))
-               die("invalid ref format: %s", argv[start]);
+               die(_("invalid ref format: %s"), argv[start]);
        return !reflog_exists(argv[start]);
 }
 
@@ -757,12 +761,12 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
  */
 
 static const char reflog_usage[] =
-"git reflog [ show | expire | delete | exists ]";
+N_("git reflog [ show | expire | delete | exists ]");
 
 int cmd_reflog(int argc, const char **argv, const char *prefix)
 {
        if (argc > 1 && !strcmp(argv[1], "-h"))
-               usage(reflog_usage);
+               usage(_(reflog_usage));
 
        /* With no command, we default to showing it. */
        if (argc < 2 || *argv[1] == '-')
index 45583683eef4d59d1c8b957a8d9a8c0834db0e48..67f8978043a43988d653092f83f21bd5baad9751 100644 (file)
@@ -197,7 +197,7 @@ static int write_oid(const struct object_id *oid, struct packed_git *pack,
 
        if (cmd->in == -1) {
                if (start_command(cmd))
-                       die("Could not start pack-objects to repack promisor objects");
+                       die(_("could not start pack-objects to repack promisor objects"));
        }
 
        xwrite(cmd->in, oid_to_hex(oid), GIT_SHA1_HEXSZ);
@@ -236,7 +236,7 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
                char *promisor_name;
                int fd;
                if (line.len != the_hash_algo->hexsz)
-                       die("repack: Expecting full hex object ID lines only from pack-objects.");
+                       die(_("repack: Expecting full hex object ID lines only from pack-objects."));
                string_list_append(names, line.buf);
 
                /*
@@ -247,13 +247,13 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
                                          line.buf);
                fd = open(promisor_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
                if (fd < 0)
-                       die_errno("unable to create '%s'", promisor_name);
+                       die_errno(_("unable to create '%s'"), promisor_name);
                close(fd);
                free(promisor_name);
        }
        fclose(out);
        if (finish_command(&cmd))
-               die("Could not finish pack-objects to repack promisor objects");
+               die(_("could not finish pack-objects to repack promisor objects"));
 }
 
 #define ALL_INTO_ONE 1
@@ -408,7 +408,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        out = xfdopen(cmd.out, "r");
        while (strbuf_getline_lf(&line, out) != EOF) {
                if (line.len != the_hash_algo->hexsz)
-                       die("repack: Expecting full hex object ID lines only from pack-objects.");
+                       die(_("repack: Expecting full hex object ID lines only from pack-objects."));
                string_list_append(&names, line.buf);
        }
        fclose(out);
@@ -417,7 +417,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                return ret;
 
        if (!names.nr && !po_args.quiet)
-               printf("Nothing new to pack.\n");
+               printf_ln(_("Nothing new to pack."));
+
+       close_all_packs(the_repository->objects);
 
        /*
         * Ok we have prepared all new packfiles.
@@ -476,13 +478,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                if (rollback_failure.nr) {
                        int i;
                        fprintf(stderr,
-                               "WARNING: Some packs in use have been renamed by\n"
-                               "WARNING: prefixing old- to their name, in order to\n"
-                               "WARNING: replace them with the new version of the\n"
-                               "WARNING: file.  But the operation failed, and the\n"
-                               "WARNING: attempt to rename them back to their\n"
-                               "WARNING: original names also failed.\n"
-                               "WARNING: Please rename them in %s manually:\n", packdir);
+                               _("WARNING: Some packs in use have been renamed by\n"
+                                 "WARNING: prefixing old- to their name, in order to\n"
+                                 "WARNING: replace them with the new version of the\n"
+                                 "WARNING: file.  But the operation failed, and the\n"
+                                 "WARNING: attempt to rename them back to their\n"
+                                 "WARNING: original names also failed.\n"
+                                 "WARNING: Please rename them in %s manually:\n"), packdir);
                        for (i = 0; i < rollback_failure.nr; i++)
                                fprintf(stderr, "WARNING:   old-%s -> %s\n",
                                        rollback_failure.items[i].string,
index d78eeaed329e5c647e5474b6a4205194b539f4b4..fd3be17b976c7d2f413b4e760236c2274f1e7639 100644 (file)
@@ -83,11 +83,12 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
        }
 
        if (!strcmp(argv[0], "clear")) {
-               rerere_clear(&merge_rr);
+               rerere_clear(the_repository, &merge_rr);
        } else if (!strcmp(argv[0], "gc"))
-               rerere_gc(&merge_rr);
+               rerere_gc(the_repository, &merge_rr);
        else if (!strcmp(argv[0], "status")) {
-               if (setup_rerere(&merge_rr, flags | RERERE_READONLY) < 0)
+               if (setup_rerere(the_repository, &merge_rr,
+                                flags | RERERE_READONLY) < 0)
                        return 0;
                for (i = 0; i < merge_rr.nr; i++)
                        printf("%s\n", merge_rr.items[i].string);
@@ -102,7 +103,8 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
                                merge_rr.items[i].util = NULL;
                }
        } else if (!strcmp(argv[0], "diff")) {
-               if (setup_rerere(&merge_rr, flags | RERERE_READONLY) < 0)
+               if (setup_rerere(the_repository, &merge_rr,
+                                flags | RERERE_READONLY) < 0)
                        return 0;
                for (i = 0; i < merge_rr.nr; i++) {
                        const char *path = merge_rr.items[i].string;
index 58166964f84ce1b78f703bb10f92f713b2cce6b4..59898c972eee5e8065c2a68b7c937cc3af11f513 100644 (file)
@@ -95,7 +95,7 @@ static int reset_index(const struct object_id *oid, int reset_type, int quiet)
 
        if (reset_type == MIXED || reset_type == HARD) {
                tree = parse_tree_indirect(oid);
-               prime_cache_tree(&the_index, tree);
+               prime_cache_tree(the_repository, the_repository->index, tree);
        }
 
        ret = 0;
@@ -413,7 +413,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                        print_new_head_line(lookup_commit_reference(the_repository, &oid));
        }
        if (!pathspec.nr)
-               remove_branch_state();
+               remove_branch_state(the_repository);
 
        return update_ref_status;
 }
index 2880ed37e3f97193d2374657346b4de52bc44954..14ef659c12a49e25bfc8b8143a7fb5b4138d1787 100644 (file)
@@ -197,7 +197,8 @@ static void finish_commit(struct commit *commit, void *data)
                free_commit_list(commit->parents);
                commit->parents = NULL;
        }
-       free_commit_buffer(commit);
+       free_commit_buffer(the_repository->parsed_objects,
+                          commit);
 }
 
 static inline void finish_object__ma(struct object *obj)
@@ -361,6 +362,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
        struct rev_list_info info;
+       struct setup_revision_opt s_r_opt = {
+               .allow_exclude_promisor_objects = 1,
+       };
        int i;
        int bisect_list = 0;
        int bisect_show_vars = 0;
@@ -374,7 +378,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        git_config(git_default_config, NULL);
        repo_init_revisions(the_repository, &revs, prefix);
        revs.abbrev = DEFAULT_ABBREV;
-       revs.allow_exclude_promisor_objects_opt = 1;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
        revs.do_not_die_on_missing_tree = 1;
 
@@ -406,7 +409,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                }
        }
 
-       argc = setup_revisions(argc, argv, &revs, NULL);
+       argc = setup_revisions(argc, argv, &revs, &s_r_opt);
 
        memset(&info, 0, sizeof(info));
        info.revs = &revs;
index c93393c89be0290ebd9e300046ff56a2ee441715..a47b53ceaff92d4079b4ea73a61ce1552ad66b05 100644 (file)
@@ -69,7 +69,8 @@ static int option_parse_m(const struct option *opt,
 
        replay->mainline = strtol(arg, &end, 10);
        if (*end || replay->mainline <= 0)
-               return opterror(opt, "expects a number greater than zero", 0);
+               return error(_("option `%s' expects a number greater than zero"),
+                            opt->long_name);
 
        return 0;
 }
@@ -195,14 +196,14 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
        if (cmd == 'q') {
                int ret = sequencer_remove_state(opts);
                if (!ret)
-                       remove_branch_state();
+                       remove_branch_state(the_repository);
                return ret;
        }
        if (cmd == 'c')
-               return sequencer_continue(opts);
+               return sequencer_continue(the_repository, opts);
        if (cmd == 'a')
-               return sequencer_rollback(opts);
-       return sequencer_pick_revisions(opts);
+               return sequencer_rollback(the_repository, opts);
+       return sequencer_pick_revisions(the_repository, opts);
 }
 
 int cmd_revert(int argc, const char **argv, const char *prefix)
index 8e3c7490f70df79497ee064ab13c0164de11fe9e..098ebf22d0d65a6f98606c7ea567467641976f6b 100644 (file)
@@ -250,7 +250,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 
        packet_reader_init(&reader, fd[0], NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE |
-                          PACKET_READ_GENTLE_ON_EOF);
+                          PACKET_READ_GENTLE_ON_EOF |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
 
        switch (discover_version(&reader)) {
        case protocol_v2:
index bdf032886912bb768cf3528efbffab4fe3029a58..be33eb83c1b72664dffc17ddffd09f79b57a2887 100644 (file)
@@ -30,6 +30,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
 {
        struct strbuf buf = STRBUF_INIT;
        enum stripspace_mode mode = STRIP_DEFAULT;
+       int nongit;
 
        const struct option options[] = {
                OPT_CMDMODE('s', "strip-comments", &mode,
@@ -46,7 +47,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
                usage_with_options(stripspace_usage, options);
 
        if (mode == STRIP_COMMENTS || mode == COMMENT_LINES) {
-               setup_git_directory_gently(NULL);
+               setup_git_directory_gently(&nongit);
                git_config(git_default_config, NULL);
        }
 
index d38113a31aeb3838190b7339475c7454e45f3a89..0e140f176ce3b559478a5bc4245bf36905c543cf 100644 (file)
@@ -1131,6 +1131,8 @@ static void deinit_submodule(const char *path, const char *prefix,
                if (!(flags & OPT_QUIET))
                        printf(format, displaypath);
 
+               submodule_unset_core_worktree(sub);
+
                strbuf_release(&sb_rm);
        }
 
@@ -1265,19 +1267,20 @@ struct submodule_alternate_setup {
        SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
 
 static int add_possible_reference_from_superproject(
-               struct alternate_object_database *alt, void *sas_cb)
+               struct object_directory *odb, void *sas_cb)
 {
        struct submodule_alternate_setup *sas = sas_cb;
+       size_t len;
 
        /*
         * If the alternate object store is another repository, try the
         * standard layout with .git/(modules/<name>)+/objects
         */
-       if (ends_with(alt->path, "/objects")) {
+       if (strip_suffix(odb->path, "/objects", &len)) {
                char *sm_alternate;
                struct strbuf sb = STRBUF_INIT;
                struct strbuf err = STRBUF_INIT;
-               strbuf_add(&sb, alt->path, strlen(alt->path) - strlen("objects"));
+               strbuf_add(&sb, odb->path, len);
 
                /*
                 * We need to end the new path with '/' to mark it as a dir,
@@ -1285,7 +1288,7 @@ static int add_possible_reference_from_superproject(
                 * as the last part of a missing submodule reference would
                 * be taken as a file name.
                 */
-               strbuf_addf(&sb, "modules/%s/", sas->submodule_name);
+               strbuf_addf(&sb, "/modules/%s/", sas->submodule_name);
 
                sm_alternate = compute_alternate_path(sb.buf, &err);
                if (sm_alternate) {
@@ -1551,7 +1554,7 @@ struct submodule_update_clone {
 #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
        SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
        NULL, NULL, NULL, \
-       NULL, 0, 0, 0, NULL, 0, 0, 0}
+       NULL, 0, 0, 0, NULL, 0, 0, 1}
 
 
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@@ -2045,7 +2048,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
        struct repository subrepo;
 
        if (argc != 2)
-               BUG("submodule--helper connect-gitdir-workingtree <name> <path>");
+               BUG("submodule--helper ensure-core-worktree <path>");
 
        path = argv[1];
 
@@ -2053,7 +2056,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
        if (!sub)
                BUG("We could get the submodule handle before?");
 
-       if (repo_submodule_init(&subrepo, the_repository, path))
+       if (repo_submodule_init(&subrepo, the_repository, sub))
                die(_("could not get a repository handle for submodule '%s'"), path);
 
        if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) {
index 5e8402617727f9cf9c017df3ea6c576e2d59240d..3f9907fcc994d248ba604509f6feb5bdc0499329 100644 (file)
@@ -9,6 +9,7 @@
 #include "refs.h"
 #include "run-command.h"
 #include "sigchain.h"
+#include "submodule.h"
 #include "refs.h"
 #include "utf8.h"
 #include "worktree.h"
@@ -724,20 +725,36 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
 static void validate_no_submodules(const struct worktree *wt)
 {
        struct index_state istate = { NULL };
+       struct strbuf path = STRBUF_INIT;
        int i, found_submodules = 0;
 
-       if (read_index_from(&istate, worktree_git_path(wt, "index"),
-                           get_worktree_git_dir(wt)) > 0) {
+       if (is_directory(worktree_git_path(wt, "modules"))) {
+               /*
+                * There could be false positives, e.g. the "modules"
+                * directory exists but is empty. But it's a rare case and
+                * this simpler check is probably good enough for now.
+                */
+               found_submodules = 1;
+       } else if (read_index_from(&istate, worktree_git_path(wt, "index"),
+                                  get_worktree_git_dir(wt)) > 0) {
                for (i = 0; i < istate.cache_nr; i++) {
                        struct cache_entry *ce = istate.cache[i];
+                       int err;
 
-                       if (S_ISGITLINK(ce->ce_mode)) {
-                               found_submodules = 1;
-                               break;
-                       }
+                       if (!S_ISGITLINK(ce->ce_mode))
+                               continue;
+
+                       strbuf_reset(&path);
+                       strbuf_addf(&path, "%s/%s", wt->path, ce->name);
+                       if (!is_submodule_populated_gently(path.buf, &err))
+                               continue;
+
+                       found_submodules = 1;
+                       break;
                }
        }
        discard_index(&istate);
+       strbuf_release(&path);
 
        if (found_submodules)
                die(_("working trees containing submodules cannot be moved or removed"));
index 88c3e16d86162518ddde771703a3d2b0fc622f3b..37b1daa6918bafb6b21faba47d25fcc6433698ba 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -127,7 +127,9 @@ static int list_refs(struct ref_list *r, int argc, const char **argv)
 /* Remember to update object flag allocation in object.h */
 #define PREREQ_MARK (1u<<16)
 
-int verify_bundle(struct bundle_header *header, int verbose)
+int verify_bundle(struct repository *r,
+                 struct bundle_header *header,
+                 int verbose)
 {
        /*
         * Do fast check, then if any prereqs are missing then go line by line
@@ -140,10 +142,10 @@ int verify_bundle(struct bundle_header *header, int verbose)
        int i, ret = 0, req_nr;
        const char *message = _("Repository lacks these prerequisite commits:");
 
-       repo_init_revisions(the_repository, &revs, NULL);
+       repo_init_revisions(r, &revs, NULL);
        for (i = 0; i < p->nr; i++) {
                struct ref_list_entry *e = p->list + i;
-               struct object *o = parse_object(the_repository, &e->oid);
+               struct object *o = parse_object(r, &e->oid);
                if (o) {
                        o->flags |= PREREQ_MARK;
                        add_pending_object(&revs, o, e->name);
@@ -168,7 +170,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
 
        for (i = 0; i < p->nr; i++) {
                struct ref_list_entry *e = p->list + i;
-               struct object *o = parse_object(the_repository, &e->oid);
+               struct object *o = parse_object(r, &e->oid);
                assert(o); /* otherwise we'd have returned early */
                if (o->flags & SHOWN)
                        continue;
@@ -180,7 +182,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
        /* Clean up objects used, as they will be reused. */
        for (i = 0; i < p->nr; i++) {
                struct ref_list_entry *e = p->list + i;
-               commit = lookup_commit_reference_gently(the_repository, &e->oid, 1);
+               commit = lookup_commit_reference_gently(r, &e->oid, 1);
                if (commit)
                        clear_commit_marks(commit, ALL_REV_FLAGS);
        }
@@ -389,8 +391,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
                         * in terms of a tag (e.g. v2.0 from the range
                         * "v1.0..v2.0")?
                         */
-                       struct commit *one = lookup_commit_reference(the_repository,
-                                                                    &oid);
+                       struct commit *one = lookup_commit_reference(revs->repo, &oid);
                        struct object *obj;
 
                        if (e->item == &(one->object)) {
@@ -423,8 +424,8 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
        return ref_count;
 }
 
-int create_bundle(struct bundle_header *header, const char *path,
-                 int argc, const char **argv)
+int create_bundle(struct repository *r, struct bundle_header *header,
+                 const char *path, int argc, const char **argv)
 {
        struct lock_file lock = LOCK_INIT;
        int bundle_fd = -1;
@@ -444,7 +445,7 @@ int create_bundle(struct bundle_header *header, const char *path,
 
        /* init revs to list objects for pack-objects later */
        save_commit_buffer = 0;
-       repo_init_revisions(the_repository, &revs, NULL);
+       repo_init_revisions(r, &revs, NULL);
 
        /* write prerequisites */
        if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv))
@@ -479,7 +480,8 @@ err:
        return -1;
 }
 
-int unbundle(struct bundle_header *header, int bundle_fd, int flags)
+int unbundle(struct repository *r, struct bundle_header *header,
+            int bundle_fd, int flags)
 {
        const char *argv_index_pack[] = {"index-pack",
                                         "--fix-thin", "--stdin", NULL, NULL};
@@ -488,7 +490,7 @@ int unbundle(struct bundle_header *header, int bundle_fd, int flags)
        if (flags & BUNDLE_VERBOSE)
                argv_index_pack[3] = "-v";
 
-       if (verify_bundle(header, 0))
+       if (verify_bundle(r, header, 0))
                return -1;
        ip.argv = argv_index_pack;
        ip.in = bundle_fd;
index e9a4cb6a74335cfd8972b19340b38e2210061a6d..781e6f5c3a837ac65e6911ec5e0ae458b8b6a5c7 100644 (file)
--- a/bundle.h
+++ b/bundle.h
@@ -18,11 +18,12 @@ struct bundle_header {
 
 int is_bundle(const char *path, int quiet);
 int read_bundle_header(const char *path, struct bundle_header *header);
-int create_bundle(struct bundle_header *header, const char *path,
-               int argc, const char **argv);
-int verify_bundle(struct bundle_header *header, int verbose);
+int create_bundle(struct repository *r, struct bundle_header *header,
+                 const char *path, int argc, const char **argv);
+int verify_bundle(struct repository *r, struct bundle_header *header, int verbose);
 #define BUNDLE_VERBOSE 1
-int unbundle(struct bundle_header *header, int bundle_fd, int flags);
+int unbundle(struct repository *r, struct bundle_header *header,
+            int bundle_fd, int flags);
 int list_bundle_refs(struct bundle_header *header,
                int argc, const char **argv);
 
index 9d454d24bca62dcdb8c941a64d09b7a53556323a..c4b8a1fa16d4e1376c1df3f2784db32c20001314 100644 (file)
@@ -448,7 +448,7 @@ int cache_tree_update(struct index_state *istate, int flags)
 }
 
 static void write_one(struct strbuf *buffer, struct cache_tree *it,
-                      const char *path, int pathlen)
+                     const char *path, int pathlen)
 {
        int i;
 
@@ -659,7 +659,9 @@ out:
        return ret;
 }
 
-static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
+static void prime_cache_tree_rec(struct repository *r,
+                                struct cache_tree *it,
+                                struct tree *tree)
 {
        struct tree_desc desc;
        struct name_entry entry;
@@ -673,24 +675,25 @@ 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(the_repository,
-                                                          entry.oid);
+                       struct tree *subtree = lookup_tree(r, &entry.oid);
                        if (!subtree->object.parsed)
                                parse_tree(subtree);
                        sub = cache_tree_sub(it, entry.path);
                        sub->cache_tree = cache_tree();
-                       prime_cache_tree_rec(sub->cache_tree, subtree);
+                       prime_cache_tree_rec(r, sub->cache_tree, subtree);
                        cnt += sub->cache_tree->entry_count;
                }
        }
        it->entry_count = cnt;
 }
 
-void prime_cache_tree(struct index_state *istate, struct tree *tree)
+void prime_cache_tree(struct repository *r,
+                     struct index_state *istate,
+                     struct tree *tree)
 {
        cache_tree_free(&istate->cache_tree);
        istate->cache_tree = cache_tree();
-       prime_cache_tree_rec(istate->cache_tree, tree);
+       prime_cache_tree_rec(r, istate->cache_tree, tree);
        istate->cache_changed |= CACHE_TREE_CHANGED;
 }
 
@@ -721,12 +724,13 @@ 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 && oideq(ent->oid, &it->oid))
+       if (it && it->entry_count > 0 && oideq(&ent->oid, &it->oid))
                return it->entry_count;
        return 0;
 }
 
-static void verify_one(struct index_state *istate,
+static void verify_one(struct repository *r,
+                      struct index_state *istate,
                       struct cache_tree *it,
                       struct strbuf *path)
 {
@@ -736,13 +740,13 @@ static void verify_one(struct index_state *istate,
 
        for (i = 0; i < it->subtree_nr; i++) {
                strbuf_addf(path, "%s/", it->down[i]->name);
-               verify_one(istate, it->down[i]->cache_tree, path);
+               verify_one(r, istate, it->down[i]->cache_tree, path);
                strbuf_setlen(path, len);
        }
 
        if (it->entry_count < 0 ||
            /* no verification on tests (t7003) that replace trees */
-           lookup_replace_object(the_repository, &it->oid) != &it->oid)
+           lookup_replace_object(r, &it->oid) != &it->oid)
                return;
 
        if (path->len) {
@@ -793,12 +797,12 @@ static void verify_one(struct index_state *istate,
        strbuf_release(&tree_buf);
 }
 
-void cache_tree_verify(struct index_state *istate)
+void cache_tree_verify(struct repository *r, struct index_state *istate)
 {
        struct strbuf path = STRBUF_INIT;
 
        if (!istate->cache_tree)
                return;
-       verify_one(istate, istate->cache_tree, &path);
+       verify_one(r, istate, istate->cache_tree, &path);
        strbuf_release(&path);
 }
index 0ab6784ffea5da0581c905738aaa66215266b5f9..326209198b60fa8a6fdbd171c46132b8a5062e04 100644 (file)
@@ -32,7 +32,7 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
 
 int cache_tree_fully_valid(struct cache_tree *);
 int cache_tree_update(struct index_state *, int);
-void cache_tree_verify(struct index_state *);
+void cache_tree_verify(struct repository *, struct index_state *);
 
 /* bitmasks to write_cache_as_tree flags */
 #define WRITE_TREE_MISSING_OK 1
@@ -47,7 +47,7 @@ void cache_tree_verify(struct index_state *);
 #define WRITE_TREE_PREFIX_ERROR (-3)
 
 int write_index_as_tree(struct object_id *oid, struct index_state *index_state, const char *index_path, int flags, const char *prefix);
-void prime_cache_tree(struct index_state *, struct tree *);
+void prime_cache_tree(struct repository *, struct index_state *, struct tree *);
 
 int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
 
diff --git a/cache.h b/cache.h
index 9ff7e56b74d99b3c7dbd5f05b43be01af4543485..bcf9fd445f47d97b07e11365421a4e39e22fc8c8 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -45,10 +45,20 @@ unsigned long git_deflate_bound(git_zstream *, unsigned long);
 /* The length in bytes and in hex digits of an object name (SHA-1 value). */
 #define GIT_SHA1_RAWSZ 20
 #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+/* The block size of SHA-1. */
+#define GIT_SHA1_BLKSZ 64
+
+/* The length in bytes and in hex digits of an object name (SHA-256 value). */
+#define GIT_SHA256_RAWSZ 32
+#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
+/* The block size of SHA-256. */
+#define GIT_SHA256_BLKSZ 64
 
 /* The length in byte and in hex digits of the largest possible hash value. */
-#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
-#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
+#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
+#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
+/* The largest possible block size for any supported hash. */
+#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
 
 struct object_id {
        unsigned char hash[GIT_MAX_RAWSZ];
@@ -745,6 +755,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
 #define ADD_CACHE_JUST_APPEND 8                /* Append only; tree.c::read_tree() */
 #define ADD_CACHE_NEW_ONLY 16          /* Do not replace existing ones */
 #define ADD_CACHE_KEEP_CACHE_TREE 32   /* Do not invalidate cache-tree */
+#define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
 extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
 extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 
@@ -1028,16 +1039,12 @@ extern const struct object_id null_oid;
 static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 {
        /*
-        * This is a temporary optimization hack. By asserting the size here,
-        * we let the compiler know that it's always going to be 20, which lets
-        * it turn this fixed-size memcmp into a few inline instructions.
-        *
-        * This will need to be extended or ripped out when we learn about
-        * hashes of different sizes.
+        * Teach the compiler that there are only two possibilities of hash size
+        * here, so that it can optimize for this case as much as possible.
         */
-       if (the_hash_algo->rawsz != 20)
-               BUG("hash size not yet supported by hashcmp");
-       return memcmp(sha1, sha2, the_hash_algo->rawsz);
+       if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
+               return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+       return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
 }
 
 static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
@@ -1047,7 +1054,13 @@ static inline int oidcmp(const struct object_id *oid1, const struct object_id *o
 
 static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
 {
-       return !hashcmp(sha1, sha2);
+       /*
+        * We write this here instead of deferring to hashcmp so that the
+        * compiler can properly inline it and avoid calling memcmp.
+        */
+       if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
+               return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+       return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
 }
 
 static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
@@ -1072,7 +1085,7 @@ static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
 
 static inline void oidcpy(struct object_id *dst, const struct object_id *src)
 {
-       hashcpy(dst->hash, src->hash);
+       memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
 }
 
 static inline struct object_id *oiddup(const struct object_id *src)
@@ -1332,6 +1345,24 @@ struct object_context {
        GET_OID_TREE | GET_OID_TREEISH | \
        GET_OID_BLOB)
 
+enum get_oid_result {
+       FOUND = 0,
+       MISSING_OBJECT = -1, /* The requested object is missing */
+       SHORT_NAME_AMBIGUOUS = -2,
+       /* The following only apply when symlinks are followed */
+       DANGLING_SYMLINK = -4, /*
+                               * The initial symlink is there, but
+                               * (transitively) points to a missing
+                               * in-tree file
+                               */
+       SYMLINK_LOOP = -5,
+       NOT_DIR = -6, /*
+                      * Somewhere along the symlink chain, a path is
+                      * requested which contains a file as a
+                      * non-final element.
+                      */
+};
+
 extern int get_oid(const char *str, struct object_id *oid);
 extern int get_oid_commit(const char *str, struct object_id *oid);
 extern int get_oid_committish(const char *str, struct object_id *oid);
@@ -1339,7 +1370,7 @@ extern int get_oid_tree(const char *str, struct object_id *oid);
 extern int get_oid_treeish(const char *str, struct object_id *oid);
 extern int get_oid_blob(const char *str, struct object_id *oid);
 extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
-extern int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
+extern enum get_oid_result get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
 
 
 typedef int each_abbrev_fn(const struct object_id *oid, void *);
@@ -1365,9 +1396,9 @@ extern int get_oid_hex(const char *hex, struct object_id *sha1);
 extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 
 /*
- * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
+ * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
  * and writes the NUL-terminated output to the buffer `out`, which must be at
- * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
+ * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
  * convenience.
  *
  * The non-`_r` variant returns a static buffer, but uses a ring of 4
@@ -1375,10 +1406,13 @@ extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
  *
  *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
  */
-extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
-extern char *oid_to_hex_r(char *out, const struct object_id *oid);
-extern char *sha1_to_hex(const unsigned char *sha1);   /* static buffer result! */
-extern char *oid_to_hex(const struct object_id *oid);  /* same static buffer as sha1_to_hex */
+char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
+char *sha1_to_hex_r(char *out, const unsigned char *sha1);
+char *oid_to_hex_r(char *out, const struct object_id *oid);
+char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);      /* static buffer result! */
+char *sha1_to_hex(const unsigned char *sha1);                                          /* same static buffer */
+char *hash_to_hex(const unsigned char *hash);                                          /* same static buffer */
+char *oid_to_hex(const struct object_id *oid);                                         /* same static buffer */
 
 /*
  * Parse a 40-character hexadecimal object ID starting from hex, updating the
@@ -1539,9 +1573,9 @@ struct checkout {
 #define CHECKOUT_INIT { NULL, "" }
 
 #define TEMPORARY_FILENAME_LENGTH 25
-extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
+extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);
 extern void enable_delayed_checkout(struct checkout *state);
-extern int finish_delayed_checkout(struct checkout *state);
+extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
 
 struct cache_def {
        struct strbuf path;
index ad7752ea6b7e3173066e346b7dd314e7707f7d8c..23d8fabe75d9d80b2811acfc20f62d210ec6bba8 100644 (file)
@@ -996,7 +996,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
        if (!userdiff)
                userdiff = userdiff_find_by_name("default");
        if (opt->flags.allow_textconv)
-               textconv = userdiff_get_textconv(userdiff);
+               textconv = userdiff_get_textconv(opt->repo, userdiff);
 
        /* Read the result of merge first */
        if (!working_tree_file)
@@ -1321,6 +1321,14 @@ static const char *path_path(void *obj)
        return path->path;
 }
 
+/*
+ * Diff stat formats which we always compute solely against the first parent.
+ */
+#define STAT_FORMAT_MASK (DIFF_FORMAT_NUMSTAT \
+                         | DIFF_FORMAT_SHORTSTAT \
+                         | DIFF_FORMAT_SUMMARY \
+                         | DIFF_FORMAT_DIRSTAT \
+                         | DIFF_FORMAT_DIFFSTAT)
 
 /* find set of paths that every parent touches */
 static struct combine_diff_path *find_paths_generic(const struct object_id *oid,
@@ -1342,8 +1350,7 @@ static struct combine_diff_path *find_paths_generic(const struct object_id *oid,
                 * show stat against the first parent even when doing
                 * combined diff.
                 */
-               int stat_opt = (output_format &
-                               (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
+               int stat_opt = output_format & STAT_FORMAT_MASK;
                if (i == 0 && stat_opt)
                        opt->output_format = stat_opt;
                else
@@ -1470,8 +1477,7 @@ void diff_tree_combined(const struct object_id *oid,
                 * show stat against the first parent even
                 * when doing combined diff.
                 */
-               stat_opt = (opt->output_format &
-                               (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
+               stat_opt = opt->output_format & STAT_FORMAT_MASK;
                if (stat_opt) {
                        diffopts.output_format = stat_opt;
 
@@ -1515,8 +1521,7 @@ void diff_tree_combined(const struct object_id *oid,
                                show_raw_diff(p, num_parent, rev);
                        needsep = 1;
                }
-               else if (opt->output_format &
-                        (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT))
+               else if (opt->output_format & STAT_FORMAT_MASK)
                        needsep = 1;
                else if (opt->output_format & DIFF_FORMAT_CALLBACK)
                        handle_combined_callback(opt, paths, num_parent, num_paths);
index 40c855f1855595fbf5b285008d3c829e6a2f41eb..47e9be0a3aad883c17221b11c972b8376a70a555 100644 (file)
 #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
 #define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
 #define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
-#define GRAPH_CHUNKID_LARGEEDGES 0x45444745 /* "EDGE" */
+#define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
 
-#define GRAPH_DATA_WIDTH 36
+#define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16)
 
 #define GRAPH_VERSION_1 0x1
 #define GRAPH_VERSION GRAPH_VERSION_1
 
-#define GRAPH_OID_VERSION_SHA1 1
-#define GRAPH_OID_LEN_SHA1 GIT_SHA1_RAWSZ
-#define GRAPH_OID_VERSION GRAPH_OID_VERSION_SHA1
-#define GRAPH_OID_LEN GRAPH_OID_LEN_SHA1
-
-#define GRAPH_OCTOPUS_EDGES_NEEDED 0x80000000
-#define GRAPH_PARENT_MISSING 0x7fffffff
+#define GRAPH_EXTRA_EDGES_NEEDED 0x80000000
 #define GRAPH_EDGE_LAST_MASK 0x7fffffff
 #define GRAPH_PARENT_NONE 0x70000000
 
 #define GRAPH_FANOUT_SIZE (4 * 256)
 #define GRAPH_CHUNKLOOKUP_WIDTH 12
 #define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * GRAPH_CHUNKLOOKUP_WIDTH \
-                       + GRAPH_FANOUT_SIZE + GRAPH_OID_LEN)
+                       + GRAPH_FANOUT_SIZE + the_hash_algo->rawsz)
 
 char *get_commit_graph_filename(const char *obj_dir)
 {
        return xstrfmt("%s/info/commit-graph", obj_dir);
 }
 
+static uint8_t oid_version(void)
+{
+       return 1;
+}
+
 static struct commit_graph *alloc_commit_graph(void)
 {
        struct commit_graph *g = xcalloc(1, sizeof(*g));
@@ -84,16 +83,10 @@ static int commit_graph_compatible(struct repository *r)
 struct commit_graph *load_commit_graph_one(const char *graph_file)
 {
        void *graph_map;
-       const unsigned char *data, *chunk_lookup;
        size_t graph_size;
        struct stat st;
-       uint32_t i;
-       struct commit_graph *graph;
+       struct commit_graph *ret;
        int fd = git_open(graph_file);
-       uint64_t last_chunk_offset;
-       uint32_t last_chunk_id;
-       uint32_t graph_signature;
-       unsigned char graph_version, hash_version;
 
        if (fd < 0)
                return NULL;
@@ -108,32 +101,60 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
                die(_("graph file %s is too small"), graph_file);
        }
        graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       ret = parse_commit_graph(graph_map, fd, graph_size);
+
+       if (!ret) {
+               munmap(graph_map, graph_size);
+               close(fd);
+               exit(1);
+       }
+
+       return ret;
+}
+
+struct commit_graph *parse_commit_graph(void *graph_map, int fd,
+                                       size_t graph_size)
+{
+       const unsigned char *data, *chunk_lookup;
+       uint32_t i;
+       struct commit_graph *graph;
+       uint64_t last_chunk_offset;
+       uint32_t last_chunk_id;
+       uint32_t graph_signature;
+       unsigned char graph_version, hash_version;
+
+       if (!graph_map)
+               return NULL;
+
+       if (graph_size < GRAPH_MIN_SIZE)
+               return NULL;
+
        data = (const unsigned char *)graph_map;
 
        graph_signature = get_be32(data);
        if (graph_signature != GRAPH_SIGNATURE) {
                error(_("graph signature %X does not match signature %X"),
                      graph_signature, GRAPH_SIGNATURE);
-               goto cleanup_fail;
+               return NULL;
        }
 
        graph_version = *(unsigned char*)(data + 4);
        if (graph_version != GRAPH_VERSION) {
                error(_("graph version %X does not match version %X"),
                      graph_version, GRAPH_VERSION);
-               goto cleanup_fail;
+               return NULL;
        }
 
        hash_version = *(unsigned char*)(data + 5);
-       if (hash_version != GRAPH_OID_VERSION) {
+       if (hash_version != oid_version()) {
                error(_("hash version %X does not match version %X"),
-                     hash_version, GRAPH_OID_VERSION);
-               goto cleanup_fail;
+                     hash_version, oid_version());
+               return NULL;
        }
 
        graph = alloc_commit_graph();
 
-       graph->hash_len = GRAPH_OID_LEN;
+       graph->hash_len = the_hash_algo->rawsz;
        graph->num_chunks = *(unsigned char*)(data + 6);
        graph->graph_fd = fd;
        graph->data = graph_map;
@@ -143,16 +164,27 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
        last_chunk_offset = 8;
        chunk_lookup = data + 8;
        for (i = 0; i < graph->num_chunks; i++) {
-               uint32_t chunk_id = get_be32(chunk_lookup + 0);
-               uint64_t chunk_offset = get_be64(chunk_lookup + 4);
+               uint32_t chunk_id;
+               uint64_t chunk_offset;
                int chunk_repeated = 0;
 
+               if (data + graph_size - chunk_lookup <
+                   GRAPH_CHUNKLOOKUP_WIDTH) {
+                       error(_("chunk lookup table entry missing; graph file may be incomplete"));
+                       free(graph);
+                       return NULL;
+               }
+
+               chunk_id = get_be32(chunk_lookup + 0);
+               chunk_offset = get_be64(chunk_lookup + 4);
+
                chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
 
-               if (chunk_offset > graph_size - GIT_MAX_RAWSZ) {
+               if (chunk_offset > graph_size - the_hash_algo->rawsz) {
                        error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
                              (uint32_t)chunk_offset);
-                       goto cleanup_fail;
+                       free(graph);
+                       return NULL;
                }
 
                switch (chunk_id) {
@@ -177,17 +209,18 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
                                graph->chunk_commit_data = data + chunk_offset;
                        break;
 
-               case GRAPH_CHUNKID_LARGEEDGES:
-                       if (graph->chunk_large_edges)
+               case GRAPH_CHUNKID_EXTRAEDGES:
+                       if (graph->chunk_extra_edges)
                                chunk_repeated = 1;
                        else
-                               graph->chunk_large_edges = data + chunk_offset;
+                               graph->chunk_extra_edges = data + chunk_offset;
                        break;
                }
 
                if (chunk_repeated) {
                        error(_("chunk id %08x appears multiple times"), chunk_id);
-                       goto cleanup_fail;
+                       free(graph);
+                       return NULL;
                }
 
                if (last_chunk_id == GRAPH_CHUNKID_OIDLOOKUP)
@@ -201,11 +234,6 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
        }
 
        return graph;
-
-cleanup_fail:
-       munmap(graph_map, graph_size);
-       close(fd);
-       exit(1);
 }
 
 static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
@@ -230,8 +258,7 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
  */
 static int prepare_commit_graph(struct repository *r)
 {
-       struct alternate_object_database *alt;
-       char *obj_dir;
+       struct object_directory *odb;
        int config_value;
 
        if (r->objects->commit_graph_attempted)
@@ -252,13 +279,11 @@ static int prepare_commit_graph(struct repository *r)
        if (!commit_graph_compatible(r))
                return 0;
 
-       obj_dir = r->objects->objectdir;
-       prepare_commit_graph_one(r, obj_dir);
        prepare_alt_odb(r);
-       for (alt = r->objects->alt_odb_list;
-            !r->objects->commit_graph && alt;
-            alt = alt->next)
-               prepare_commit_graph_one(r, alt->path);
+       for (odb = r->objects->odb;
+            !r->objects->commit_graph && odb;
+            odb = odb->next)
+               prepare_commit_graph_one(r, odb->path);
        return !!r->objects->commit_graph;
 }
 
@@ -292,7 +317,8 @@ static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t
                            g->chunk_oid_lookup, g->hash_len, pos);
 }
 
-static struct commit_list **insert_parent_or_die(struct commit_graph *g,
+static struct commit_list **insert_parent_or_die(struct repository *r,
+                                                struct commit_graph *g,
                                                 uint64_t pos,
                                                 struct commit_list **pptr)
 {
@@ -303,7 +329,7 @@ static struct commit_list **insert_parent_or_die(struct commit_graph *g,
                die("invalid parent position %"PRIu64, pos);
 
        hashcpy(oid.hash, g->chunk_oid_lookup + g->hash_len * pos);
-       c = lookup_commit(the_repository, &oid);
+       c = lookup_commit(r, &oid);
        if (!c)
                die(_("could not find commit %s"), oid_to_hex(&oid));
        c->graph_pos = pos;
@@ -317,7 +343,9 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
        item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
 }
 
-static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uint32_t pos)
+static int fill_commit_in_graph(struct repository *r,
+                               struct commit *item,
+                               struct commit_graph *g, uint32_t pos)
 {
        uint32_t edge_value;
        uint32_t *parent_data_ptr;
@@ -341,21 +369,21 @@ static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uin
        edge_value = get_be32(commit_data + g->hash_len);
        if (edge_value == GRAPH_PARENT_NONE)
                return 1;
-       pptr = insert_parent_or_die(g, edge_value, pptr);
+       pptr = insert_parent_or_die(r, g, edge_value, pptr);
 
        edge_value = get_be32(commit_data + g->hash_len + 4);
        if (edge_value == GRAPH_PARENT_NONE)
                return 1;
-       if (!(edge_value & GRAPH_OCTOPUS_EDGES_NEEDED)) {
-               pptr = insert_parent_or_die(g, edge_value, pptr);
+       if (!(edge_value & GRAPH_EXTRA_EDGES_NEEDED)) {
+               pptr = insert_parent_or_die(r, g, edge_value, pptr);
                return 1;
        }
 
-       parent_data_ptr = (uint32_t*)(g->chunk_large_edges +
+       parent_data_ptr = (uint32_t*)(g->chunk_extra_edges +
                          4 * (uint64_t)(edge_value & GRAPH_EDGE_LAST_MASK));
        do {
                edge_value = get_be32(parent_data_ptr);
-               pptr = insert_parent_or_die(g,
+               pptr = insert_parent_or_die(r, g,
                                            edge_value & GRAPH_EDGE_LAST_MASK,
                                            pptr);
                parent_data_ptr++;
@@ -374,7 +402,9 @@ static int find_commit_in_graph(struct commit *item, struct commit_graph *g, uin
        }
 }
 
-static int parse_commit_in_graph_one(struct commit_graph *g, struct commit *item)
+static int parse_commit_in_graph_one(struct repository *r,
+                                    struct commit_graph *g,
+                                    struct commit *item)
 {
        uint32_t pos;
 
@@ -382,7 +412,7 @@ static int parse_commit_in_graph_one(struct commit_graph *g, struct commit *item
                return 1;
 
        if (find_commit_in_graph(item, g, &pos))
-               return fill_commit_in_graph(item, g, pos);
+               return fill_commit_in_graph(r, item, g, pos);
 
        return 0;
 }
@@ -391,7 +421,7 @@ int parse_commit_in_graph(struct repository *r, struct commit *item)
 {
        if (!prepare_commit_graph(r))
                return 0;
-       return parse_commit_in_graph_one(r->objects->commit_graph, item);
+       return parse_commit_in_graph_one(r, r->objects->commit_graph, item);
 }
 
 void load_commit_graph_info(struct repository *r, struct commit *item)
@@ -403,19 +433,22 @@ void load_commit_graph_info(struct repository *r, struct commit *item)
                fill_commit_graph_info(item, r->objects->commit_graph, pos);
 }
 
-static struct tree *load_tree_for_commit(struct commit_graph *g, struct commit *c)
+static struct tree *load_tree_for_commit(struct repository *r,
+                                        struct commit_graph *g,
+                                        struct commit *c)
 {
        struct object_id oid;
        const unsigned char *commit_data = g->chunk_commit_data +
                                           GRAPH_DATA_WIDTH * (c->graph_pos);
 
        hashcpy(oid.hash, commit_data);
-       c->maybe_tree = lookup_tree(the_repository, &oid);
+       c->maybe_tree = lookup_tree(r, &oid);
 
        return c->maybe_tree;
 }
 
-static struct tree *get_commit_tree_in_graph_one(struct commit_graph *g,
+static struct tree *get_commit_tree_in_graph_one(struct repository *r,
+                                                struct commit_graph *g,
                                                 const struct commit *c)
 {
        if (c->maybe_tree)
@@ -423,17 +456,19 @@ static struct tree *get_commit_tree_in_graph_one(struct commit_graph *g,
        if (c->graph_pos == COMMIT_NOT_FROM_GRAPH)
                BUG("get_commit_tree_in_graph_one called from non-commit-graph commit");
 
-       return load_tree_for_commit(g, (struct commit *)c);
+       return load_tree_for_commit(r, g, (struct commit *)c);
 }
 
 struct tree *get_commit_tree_in_graph(struct repository *r, const struct commit *c)
 {
-       return get_commit_tree_in_graph_one(r->objects->commit_graph, c);
+       return get_commit_tree_in_graph_one(r, r->objects->commit_graph, c);
 }
 
 static void write_graph_chunk_fanout(struct hashfile *f,
                                     struct commit **commits,
-                                    int nr_commits)
+                                    int nr_commits,
+                                    struct progress *progress,
+                                    uint64_t *progress_cnt)
 {
        int i, count = 0;
        struct commit **list = commits;
@@ -447,6 +482,7 @@ static void write_graph_chunk_fanout(struct hashfile *f,
                while (count < nr_commits) {
                        if ((*list)->object.oid.hash[0] != i)
                                break;
+                       display_progress(progress, ++*progress_cnt);
                        count++;
                        list++;
                }
@@ -456,12 +492,16 @@ static void write_graph_chunk_fanout(struct hashfile *f,
 }
 
 static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
-                                  struct commit **commits, int nr_commits)
+                                  struct commit **commits, int nr_commits,
+                                  struct progress *progress,
+                                  uint64_t *progress_cnt)
 {
        struct commit **list = commits;
        int count;
-       for (count = 0; count < nr_commits; count++, list++)
+       for (count = 0; count < nr_commits; count++, list++) {
+               display_progress(progress, ++*progress_cnt);
                hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
+       }
 }
 
 static const unsigned char *commit_to_sha1(size_t index, void *table)
@@ -471,7 +511,9 @@ static const unsigned char *commit_to_sha1(size_t index, void *table)
 }
 
 static void write_graph_chunk_data(struct hashfile *f, int hash_len,
-                                  struct commit **commits, int nr_commits)
+                                  struct commit **commits, int nr_commits,
+                                  struct progress *progress,
+                                  uint64_t *progress_cnt)
 {
        struct commit **list = commits;
        struct commit **last = commits + nr_commits;
@@ -481,6 +523,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                struct commit_list *parent;
                int edge_value;
                uint32_t packedDate[2];
+               display_progress(progress, ++*progress_cnt);
 
                parse_commit(*list);
                hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
@@ -496,7 +539,9 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                                              commit_to_sha1);
 
                        if (edge_value < 0)
-                               edge_value = GRAPH_PARENT_MISSING;
+                               BUG("missing parent %s for commit %s",
+                                   oid_to_hex(&parent->item->object.oid),
+                                   oid_to_hex(&(*list)->object.oid));
                }
 
                hashwrite_be32(f, edge_value);
@@ -507,19 +552,21 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                if (!parent)
                        edge_value = GRAPH_PARENT_NONE;
                else if (parent->next)
-                       edge_value = GRAPH_OCTOPUS_EDGES_NEEDED | num_extra_edges;
+                       edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
                else {
                        edge_value = sha1_pos(parent->item->object.oid.hash,
                                              commits,
                                              nr_commits,
                                              commit_to_sha1);
                        if (edge_value < 0)
-                               edge_value = GRAPH_PARENT_MISSING;
+                               BUG("missing parent %s for commit %s",
+                                   oid_to_hex(&parent->item->object.oid),
+                                   oid_to_hex(&(*list)->object.oid));
                }
 
                hashwrite_be32(f, edge_value);
 
-               if (edge_value & GRAPH_OCTOPUS_EDGES_NEEDED) {
+               if (edge_value & GRAPH_EXTRA_EDGES_NEEDED) {
                        do {
                                num_extra_edges++;
                                parent = parent->next;
@@ -540,9 +587,11 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
        }
 }
 
-static void write_graph_chunk_large_edges(struct hashfile *f,
+static void write_graph_chunk_extra_edges(struct hashfile *f,
                                          struct commit **commits,
-                                         int nr_commits)
+                                         int nr_commits,
+                                         struct progress *progress,
+                                         uint64_t *progress_cnt)
 {
        struct commit **list = commits;
        struct commit **last = commits + nr_commits;
@@ -550,6 +599,9 @@ static void write_graph_chunk_large_edges(struct hashfile *f,
 
        while (list < last) {
                int num_parents = 0;
+
+               display_progress(progress, ++*progress_cnt);
+
                for (parent = (*list)->parents; num_parents < 3 && parent;
                     parent = parent->next)
                        num_parents++;
@@ -567,7 +619,9 @@ static void write_graph_chunk_large_edges(struct hashfile *f,
                                                  commit_to_sha1);
 
                        if (edge_value < 0)
-                               edge_value = GRAPH_PARENT_MISSING;
+                               BUG("missing parent %s for commit %s",
+                                   oid_to_hex(&parent->item->object.oid),
+                                   oid_to_hex(&(*list)->object.oid));
                        else if (!parent->next)
                                edge_value |= GRAPH_LAST_EDGE;
 
@@ -644,33 +698,40 @@ static void close_reachable(struct packed_oid_list *oids, int report_progress)
        int i;
        struct commit *commit;
        struct progress *progress = NULL;
-       int j = 0;
 
        if (report_progress)
                progress = start_delayed_progress(
-                       _("Annotating commits in commit graph"), 0);
+                       _("Loading known commits in commit graph"), oids->nr);
        for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, ++j);
+               display_progress(progress, i + 1);
                commit = lookup_commit(the_repository, &oids->list[i]);
                if (commit)
                        commit->object.flags |= UNINTERESTING;
        }
+       stop_progress(&progress);
 
        /*
         * As this loop runs, oids->nr may grow, but not more
         * than the number of missing commits in the reachable
         * closure.
         */
+       if (report_progress)
+               progress = start_delayed_progress(
+                       _("Expanding reachable commits in commit graph"), oids->nr);
        for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, ++j);
+               display_progress(progress, i + 1);
                commit = lookup_commit(the_repository, &oids->list[i]);
 
                if (commit && !parse_commit(commit))
                        add_missing_parents(oids, commit);
        }
+       stop_progress(&progress);
 
+       if (report_progress)
+               progress = start_delayed_progress(
+                       _("Clearing commit marks in commit graph"), oids->nr);
        for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, ++j);
+               display_progress(progress, i + 1);
                commit = lookup_commit(the_repository, &oids->list[i]);
 
                if (commit)
@@ -764,12 +825,17 @@ void write_commit_graph(const char *obj_dir,
        int num_extra_edges;
        struct commit_list *parent;
        struct progress *progress = NULL;
+       const unsigned hashsz = the_hash_algo->rawsz;
+       uint64_t progress_cnt = 0;
+       struct strbuf progress_title = STRBUF_INIT;
+       unsigned long approx_nr_objects;
 
        if (!commit_graph_compatible(the_repository))
                return;
 
        oids.nr = 0;
-       oids.alloc = approximate_object_count() / 32;
+       approx_nr_objects = approximate_object_count();
+       oids.alloc = approx_nr_objects / 32;
        oids.progress = NULL;
        oids.progress_done = 0;
 
@@ -799,8 +865,12 @@ void write_commit_graph(const char *obj_dir,
                strbuf_addf(&packname, "%s/pack/", obj_dir);
                dirlen = packname.len;
                if (report_progress) {
-                       oids.progress = start_delayed_progress(
-                               _("Finding commits for commit graph"), 0);
+                       strbuf_addf(&progress_title,
+                                   Q_("Finding commits for commit graph in %d pack",
+                                      "Finding commits for commit graph in %d packs",
+                                      pack_indexes->nr),
+                                   pack_indexes->nr);
+                       oids.progress = start_delayed_progress(progress_title.buf, 0);
                        oids.progress_done = 0;
                }
                for (i = 0; i < pack_indexes->nr; i++) {
@@ -812,19 +882,26 @@ void write_commit_graph(const char *obj_dir,
                                die(_("error adding pack %s"), packname.buf);
                        if (open_pack_index(p))
                                die(_("error opening index for %s"), packname.buf);
-                       for_each_object_in_pack(p, add_packed_commits, &oids, 0);
+                       for_each_object_in_pack(p, add_packed_commits, &oids,
+                                               FOR_EACH_OBJECT_PACK_ORDER);
                        close_pack(p);
                        free(p);
                }
                stop_progress(&oids.progress);
+               strbuf_reset(&progress_title);
                strbuf_release(&packname);
        }
 
        if (commit_hex) {
-               if (report_progress)
-                       progress = start_delayed_progress(
-                               _("Finding commits for commit graph"),
-                               commit_hex->nr);
+               if (report_progress) {
+                       strbuf_addf(&progress_title,
+                                   Q_("Finding commits for commit graph from %d ref",
+                                      "Finding commits for commit graph from %d refs",
+                                      commit_hex->nr),
+                                   commit_hex->nr);
+                       progress = start_delayed_progress(progress_title.buf,
+                                                         commit_hex->nr);
+               }
                for (i = 0; i < commit_hex->nr; i++) {
                        const char *end;
                        struct object_id oid;
@@ -844,27 +921,38 @@ void write_commit_graph(const char *obj_dir,
                        }
                }
                stop_progress(&progress);
+               strbuf_reset(&progress_title);
        }
 
        if (!pack_indexes && !commit_hex) {
                if (report_progress)
                        oids.progress = start_delayed_progress(
-                               _("Finding commits for commit graph"), 0);
-               for_each_packed_object(add_packed_commits, &oids, 0);
+                               _("Finding commits for commit graph among packed objects"),
+                               approx_nr_objects);
+               for_each_packed_object(add_packed_commits, &oids,
+                                      FOR_EACH_OBJECT_PACK_ORDER);
+               if (oids.progress_done < approx_nr_objects)
+                       display_progress(oids.progress, approx_nr_objects);
                stop_progress(&oids.progress);
        }
 
        close_reachable(&oids, report_progress);
 
+       if (report_progress)
+               progress = start_delayed_progress(
+                       _("Counting distinct commits in commit graph"),
+                       oids.nr);
+       display_progress(progress, 0); /* TODO: Measure QSORT() progress */
        QSORT(oids.list, oids.nr, commit_compare);
-
        count_distinct = 1;
        for (i = 1; i < oids.nr; i++) {
+               display_progress(progress, i + 1);
                if (!oideq(&oids.list[i - 1], &oids.list[i]))
                        count_distinct++;
        }
+       stop_progress(&progress);
 
-       if (count_distinct >= GRAPH_PARENT_MISSING)
+       if (count_distinct >= GRAPH_EDGE_LAST_MASK)
                die(_("the commit graph format cannot write %d commits"), count_distinct);
 
        commits.nr = 0;
@@ -872,8 +960,13 @@ void write_commit_graph(const char *obj_dir,
        ALLOC_ARRAY(commits.list, commits.alloc);
 
        num_extra_edges = 0;
+       if (report_progress)
+               progress = start_delayed_progress(
+                       _("Finding extra edges in commit graph"),
+                       oids.nr);
        for (i = 0; i < oids.nr; i++) {
                int num_parents = 0;
+               display_progress(progress, i + 1);
                if (i > 0 && oideq(&oids.list[i - 1], &oids.list[i]))
                        continue;
 
@@ -890,8 +983,9 @@ void write_commit_graph(const char *obj_dir,
                commits.nr++;
        }
        num_chunks = num_extra_edges ? 4 : 3;
+       stop_progress(&progress);
 
-       if (commits.nr >= GRAPH_PARENT_MISSING)
+       if (commits.nr >= GRAPH_EDGE_LAST_MASK)
                die(_("too many commits to write graph"));
 
        compute_generation_numbers(&commits, report_progress);
@@ -909,7 +1003,7 @@ void write_commit_graph(const char *obj_dir,
        hashwrite_be32(f, GRAPH_SIGNATURE);
 
        hashwrite_u8(f, GRAPH_VERSION);
-       hashwrite_u8(f, GRAPH_OID_VERSION);
+       hashwrite_u8(f, oid_version());
        hashwrite_u8(f, num_chunks);
        hashwrite_u8(f, 0); /* unused padding byte */
 
@@ -917,15 +1011,15 @@ void write_commit_graph(const char *obj_dir,
        chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
        chunk_ids[2] = GRAPH_CHUNKID_DATA;
        if (num_extra_edges)
-               chunk_ids[3] = GRAPH_CHUNKID_LARGEEDGES;
+               chunk_ids[3] = GRAPH_CHUNKID_EXTRAEDGES;
        else
                chunk_ids[3] = 0;
        chunk_ids[4] = 0;
 
        chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
        chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
-       chunk_offsets[2] = chunk_offsets[1] + GRAPH_OID_LEN * commits.nr;
-       chunk_offsets[3] = chunk_offsets[2] + (GRAPH_OID_LEN + 16) * commits.nr;
+       chunk_offsets[2] = chunk_offsets[1] + hashsz * commits.nr;
+       chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * commits.nr;
        chunk_offsets[4] = chunk_offsets[3] + 4 * num_extra_edges;
 
        for (i = 0; i <= num_chunks; i++) {
@@ -937,10 +1031,23 @@ void write_commit_graph(const char *obj_dir,
                hashwrite(f, chunk_write, 12);
        }
 
-       write_graph_chunk_fanout(f, commits.list, commits.nr);
-       write_graph_chunk_oids(f, GRAPH_OID_LEN, commits.list, commits.nr);
-       write_graph_chunk_data(f, GRAPH_OID_LEN, commits.list, commits.nr);
-       write_graph_chunk_large_edges(f, commits.list, commits.nr);
+       if (report_progress) {
+               strbuf_addf(&progress_title,
+                           Q_("Writing out commit graph in %d pass",
+                              "Writing out commit graph in %d passes",
+                              num_chunks),
+                           num_chunks);
+               progress = start_delayed_progress(
+                       progress_title.buf,
+                       num_chunks * commits.nr);
+       }
+       write_graph_chunk_fanout(f, commits.list, commits.nr, progress, &progress_cnt);
+       write_graph_chunk_oids(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
+       write_graph_chunk_data(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
+       if (num_extra_edges)
+               write_graph_chunk_extra_edges(f, commits.list, commits.nr, progress, &progress_cnt);
+       stop_progress(&progress);
+       strbuf_release(&progress_title);
 
        close_commit_graph(the_repository);
        finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
@@ -1025,7 +1132,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
                }
 
                graph_commit = lookup_commit(r, &cur_oid);
-               if (!parse_commit_in_graph_one(g, graph_commit))
+               if (!parse_commit_in_graph_one(r, g, graph_commit))
                        graph_report("failed to parse %s from commit-graph",
                                     oid_to_hex(&cur_oid));
        }
@@ -1061,7 +1168,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
                        continue;
                }
 
-               if (!oideq(&get_commit_tree_in_graph_one(g, graph_commit)->object.oid,
+               if (!oideq(&get_commit_tree_in_graph_one(r, g, graph_commit)->object.oid,
                           get_commit_tree_oid(odb_commit)))
                        graph_report("root tree OID for commit %s in commit-graph is %s != %s",
                                     oid_to_hex(&cur_oid),
index 9db40b4d3aadb75b4dcea5027d07d373bdf169e1..096d8bac340514916c852268ef37c7b97190799a 100644 (file)
@@ -49,11 +49,14 @@ struct commit_graph {
        const uint32_t *chunk_oid_fanout;
        const unsigned char *chunk_oid_lookup;
        const unsigned char *chunk_commit_data;
-       const unsigned char *chunk_large_edges;
+       const unsigned char *chunk_extra_edges;
 };
 
 struct commit_graph *load_commit_graph_one(const char *graph_file);
 
+struct commit_graph *parse_commit_graph(void *graph_map, int fd,
+                                       size_t graph_size);
+
 /*
  * Return 1 if and only if the repository has a commit-graph
  * file and generation numbers are computed in that file.
index d5a39defd3d5144f83d4f48e43e54e1f7f49ab29..3ea174788a4f0229460bad65373e974cc3f87c64 100644 (file)
@@ -30,7 +30,8 @@ static int queue_has_nonstale(struct prio_queue *queue)
 }
 
 /* all input commits in one and twos[] must have been parsed! */
-static struct commit_list *paint_down_to_common(struct commit *one, int n,
+static struct commit_list *paint_down_to_common(struct repository *r,
+                                               struct commit *one, int n,
                                                struct commit **twos,
                                                int min_generation)
 {
@@ -83,7 +84,7 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n,
                        parents = parents->next;
                        if ((p->object.flags & flags) == flags)
                                continue;
-                       if (parse_commit(p))
+                       if (repo_parse_commit(r, p))
                                return NULL;
                        p->object.flags |= flags;
                        prio_queue_put(&queue, p);
@@ -94,7 +95,9 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n,
        return result;
 }
 
-static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
+static struct commit_list *merge_bases_many(struct repository *r,
+                                           struct commit *one, int n,
+                                           struct commit **twos)
 {
        struct commit_list *list = NULL;
        struct commit_list *result = NULL;
@@ -109,14 +112,14 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
                        return commit_list_insert(one, &result);
        }
 
-       if (parse_commit(one))
+       if (repo_parse_commit(r, one))
                return NULL;
        for (i = 0; i < n; i++) {
-               if (parse_commit(twos[i]))
+               if (repo_parse_commit(r, twos[i]))
                        return NULL;
        }
 
-       list = paint_down_to_common(one, n, twos, 0);
+       list = paint_down_to_common(r, one, n, twos, 0);
 
        while (list) {
                struct commit *commit = pop_commit(&list);
@@ -153,7 +156,7 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
        return ret;
 }
 
-static int remove_redundant(struct commit **array, int cnt)
+static int remove_redundant(struct repository *r, struct commit **array, int cnt)
 {
        /*
         * Some commit in the array may be an ancestor of
@@ -171,7 +174,7 @@ static int remove_redundant(struct commit **array, int cnt)
        ALLOC_ARRAY(filled_index, cnt - 1);
 
        for (i = 0; i < cnt; i++)
-               parse_commit(array[i]);
+               repo_parse_commit(r, array[i]);
        for (i = 0; i < cnt; i++) {
                struct commit_list *common;
                uint32_t min_generation = array[i]->generation;
@@ -187,8 +190,8 @@ static int remove_redundant(struct commit **array, int cnt)
                        if (array[j]->generation < min_generation)
                                min_generation = array[j]->generation;
                }
-               common = paint_down_to_common(array[i], filled, work,
-                                             min_generation);
+               common = paint_down_to_common(r, array[i], filled,
+                                             work, min_generation);
                if (array[i]->object.flags & PARENT2)
                        redundant[i] = 1;
                for (j = 0; j < filled; j++)
@@ -213,7 +216,8 @@ static int remove_redundant(struct commit **array, int cnt)
        return filled;
 }
 
-static struct commit_list *get_merge_bases_many_0(struct commit *one,
+static struct commit_list *get_merge_bases_many_0(struct repository *r,
+                                                 struct commit *one,
                                                  int n,
                                                  struct commit **twos,
                                                  int cleanup)
@@ -223,7 +227,7 @@ static struct commit_list *get_merge_bases_many_0(struct commit *one,
        struct commit_list *result;
        int cnt, i;
 
-       result = merge_bases_many(one, n, twos);
+       result = merge_bases_many(r, one, n, twos);
        for (i = 0; i < n; i++) {
                if (one == twos[i])
                        return result;
@@ -246,7 +250,7 @@ static struct commit_list *get_merge_bases_many_0(struct commit *one,
        clear_commit_marks(one, all_flags);
        clear_commit_marks_many(n, twos, all_flags);
 
-       cnt = remove_redundant(rslt, cnt);
+       cnt = remove_redundant(r, rslt, cnt);
        result = NULL;
        for (i = 0; i < cnt; i++)
                commit_list_insert_by_date(rslt[i], &result);
@@ -254,23 +258,27 @@ static struct commit_list *get_merge_bases_many_0(struct commit *one,
        return result;
 }
 
-struct commit_list *get_merge_bases_many(struct commit *one,
-                                        int n,
-                                        struct commit **twos)
+struct commit_list *repo_get_merge_bases_many(struct repository *r,
+                                             struct commit *one,
+                                             int n,
+                                             struct commit **twos)
 {
-       return get_merge_bases_many_0(one, n, twos, 1);
+       return get_merge_bases_many_0(r, one, n, twos, 1);
 }
 
-struct commit_list *get_merge_bases_many_dirty(struct commit *one,
-                                              int n,
-                                              struct commit **twos)
+struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
+                                                   struct commit *one,
+                                                   int n,
+                                                   struct commit **twos)
 {
-       return get_merge_bases_many_0(one, n, twos, 0);
+       return get_merge_bases_many_0(r, one, n, twos, 0);
 }
 
-struct commit_list *get_merge_bases(struct commit *one, struct commit *two)
+struct commit_list *repo_get_merge_bases(struct repository *r,
+                                        struct commit *one,
+                                        struct commit *two)
 {
-       return get_merge_bases_many_0(one, 1, &two, 1);
+       return get_merge_bases_many_0(r, one, 1, &two, 1);
 }
 
 /*
@@ -304,16 +312,17 @@ int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
 /*
  * Is "commit" an ancestor of one of the "references"?
  */
-int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference)
+int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
+                            int nr_reference, struct commit **reference)
 {
        struct commit_list *bases;
        int ret = 0, i;
        uint32_t min_generation = GENERATION_NUMBER_INFINITY;
 
-       if (parse_commit(commit))
+       if (repo_parse_commit(r, commit))
                return ret;
        for (i = 0; i < nr_reference; i++) {
-               if (parse_commit(reference[i]))
+               if (repo_parse_commit(r, reference[i]))
                        return ret;
                if (reference[i]->generation < min_generation)
                        min_generation = reference[i]->generation;
@@ -322,7 +331,9 @@ int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit *
        if (commit->generation > min_generation)
                return ret;
 
-       bases = paint_down_to_common(commit, nr_reference, reference, commit->generation);
+       bases = paint_down_to_common(r, commit,
+                                    nr_reference, reference,
+                                    commit->generation);
        if (commit->object.flags & PARENT2)
                ret = 1;
        clear_commit_marks(commit, all_flags);
@@ -334,9 +345,11 @@ int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit *
 /*
  * Is "commit" an ancestor of (i.e. reachable from) the "reference"?
  */
-int in_merge_bases(struct commit *commit, struct commit *reference)
+int repo_in_merge_bases(struct repository *r,
+                       struct commit *commit,
+                       struct commit *reference)
 {
-       return in_merge_bases_many(commit, 1, &reference);
+       return repo_in_merge_bases_many(r, commit, 1, &reference);
 }
 
 struct commit_list *reduce_heads(struct commit_list *heads)
@@ -365,7 +378,7 @@ struct commit_list *reduce_heads(struct commit_list *heads)
                        p->item->object.flags &= ~STALE;
                }
        }
-       num_head = remove_redundant(array, num_head);
+       num_head = remove_redundant(the_repository, array, num_head);
        for (i = 0; i < num_head; i++)
                tail = &commit_list_insert(array[i], tail)->next;
        free(array);
index fb8082a2ece94a94cf2f9a55ba95950caa41a914..99a43e8b64fc803d7b7f4d09d11c2ec31fdb0a76 100644 (file)
@@ -9,21 +9,35 @@ struct ref_filter;
 struct object_id;
 struct object_array;
 
-struct commit_list *get_merge_bases_many(struct commit *one,
-                                        int n,
-                                        struct commit **twos);
-struct commit_list *get_merge_bases_many_dirty(struct commit *one,
-                                              int n,
-                                              struct commit **twos);
-struct commit_list *get_merge_bases(struct commit *one, struct commit *two);
-struct commit_list *get_octopus_merge_bases(struct commit_list *in);
-
+struct commit_list *repo_get_merge_bases(struct repository *r,
+                                        struct commit *rev1,
+                                        struct commit *rev2);
+struct commit_list *repo_get_merge_bases_many(struct repository *r,
+                                             struct commit *one, int n,
+                                             struct commit **twos);
 /* To be used only when object flags after this call no longer matter */
-struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct commit **twos);
+struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
+                                                   struct commit *one, int n,
+                                                   struct commit **twos);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define get_merge_bases(r1, r2)           repo_get_merge_bases(the_repository, r1, r2)
+#define get_merge_bases_many(one, n, two) repo_get_merge_bases_many(the_repository, one, n, two)
+#define get_merge_bases_many_dirty(one, n, twos) repo_get_merge_bases_many_dirty(the_repository, one, n, twos)
+#endif
+
+struct commit_list *get_octopus_merge_bases(struct commit_list *in);
 
 int is_descendant_of(struct commit *commit, struct commit_list *with_commit);
-int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference);
-int in_merge_bases(struct commit *commit, struct commit *reference);
+int repo_in_merge_bases(struct repository *r,
+                       struct commit *commit,
+                       struct commit *reference);
+int repo_in_merge_bases_many(struct repository *r,
+                            struct commit *commit,
+                            int nr_reference, struct commit **reference);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define in_merge_bases(c1, c2) repo_in_merge_bases(the_repository, c1, c2)
+#define in_merge_bases_many(c1, n, cs) repo_in_merge_bases_many(the_repository, c1, n, cs)
+#endif
 
 /*
  * Takes a list of commits and returns a new list where those
index d13a7bc3746406bdaf0bda0975c25b40111afe47..a5333c7ac6c373a13f9298b36be5ff94a90a3e3f 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -299,13 +299,15 @@ const void *get_cached_commit_buffer(struct repository *r, const struct commit *
        return v->buffer;
 }
 
-const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
+const void *repo_get_commit_buffer(struct repository *r,
+                                  const struct commit *commit,
+                                  unsigned long *sizep)
 {
-       const void *ret = get_cached_commit_buffer(the_repository, commit, sizep);
+       const void *ret = get_cached_commit_buffer(r, commit, sizep);
        if (!ret) {
                enum object_type type;
                unsigned long size;
-               ret = read_object_file(&commit->object.oid, &type, &size);
+               ret = repo_read_object_file(r, &commit->object.oid, &type, &size);
                if (!ret)
                        die("cannot read commit object %s",
                            oid_to_hex(&commit->object.oid));
@@ -318,18 +320,20 @@ const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
        return ret;
 }
 
-void unuse_commit_buffer(const struct commit *commit, const void *buffer)
+void repo_unuse_commit_buffer(struct repository *r,
+                             const struct commit *commit,
+                             const void *buffer)
 {
        struct commit_buffer *v = buffer_slab_peek(
-               the_repository->parsed_objects->buffer_slab, commit);
+               r->parsed_objects->buffer_slab, commit);
        if (!(v && v->buffer == buffer))
                free((void *)buffer);
 }
 
-void free_commit_buffer(struct commit *commit)
+void free_commit_buffer(struct parsed_object_pool *pool, struct commit *commit)
 {
        struct commit_buffer *v = buffer_slab_peek(
-               the_repository->parsed_objects->buffer_slab, commit);
+               pool->buffer_slab, commit);
        if (v) {
                FREE_AND_NULL(v->buffer);
                v->size = 0;
@@ -352,13 +356,12 @@ struct object_id *get_commit_tree_oid(const struct commit *commit)
        return &get_commit_tree(commit)->object.oid;
 }
 
-void release_commit_memory(struct commit *c)
+void release_commit_memory(struct parsed_object_pool *pool, struct commit *c)
 {
        c->maybe_tree = NULL;
        c->index = 0;
-       free_commit_buffer(c);
+       free_commit_buffer(pool, c);
        free_commit_list(c->parents);
-       /* TODO: what about commit->util? */
 
        c->object.parsed = 0;
 }
@@ -445,7 +448,10 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
        return 0;
 }
 
-int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_commit_graph)
+int repo_parse_commit_internal(struct repository *r,
+                              struct commit *item,
+                              int quiet_on_missing,
+                              int use_commit_graph)
 {
        enum object_type type;
        void *buffer;
@@ -456,9 +462,9 @@ int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_com
                return -1;
        if (item->object.parsed)
                return 0;
-       if (use_commit_graph && parse_commit_in_graph(the_repository, item))
+       if (use_commit_graph && parse_commit_in_graph(r, item))
                return 0;
-       buffer = read_object_file(&item->object.oid, &type, &size);
+       buffer = repo_read_object_file(r, &item->object.oid, &type, &size);
        if (!buffer)
                return quiet_on_missing ? -1 :
                        error("Could not read %s",
@@ -469,18 +475,19 @@ int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_com
                             oid_to_hex(&item->object.oid));
        }
 
-       ret = parse_commit_buffer(the_repository, item, buffer, size, 0);
+       ret = parse_commit_buffer(r, item, buffer, size, 0);
        if (save_commit_buffer && !ret) {
-               set_commit_buffer(the_repository, item, buffer, size);
+               set_commit_buffer(r, item, buffer, size);
                return 0;
        }
        free(buffer);
        return ret;
 }
 
-int parse_commit_gently(struct commit *item, int quiet_on_missing)
+int repo_parse_commit_gently(struct repository *r,
+                            struct commit *item, int quiet_on_missing)
 {
-       return parse_commit_internal(item, quiet_on_missing, 1);
+       return repo_parse_commit_internal(r, item, quiet_on_missing, 1);
 }
 
 void parse_commit_or_die(struct commit *item)
index 98664536cb82c65f04a1742c3e5c1e269afc9493..42728c2906608a9f4f1724e02b16d913b74b8728 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -80,12 +80,21 @@ struct commit *lookup_commit_reference_by_name(const char *name);
 struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name);
 
 int parse_commit_buffer(struct repository *r, struct commit *item, const void *buffer, unsigned long size, int check_graph);
-int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_commit_graph);
-int parse_commit_gently(struct commit *item, int quiet_on_missing);
-static inline int parse_commit(struct commit *item)
+int repo_parse_commit_internal(struct repository *r, struct commit *item,
+                              int quiet_on_missing, int use_commit_graph);
+int repo_parse_commit_gently(struct repository *r,
+                            struct commit *item,
+                            int quiet_on_missing);
+static inline int repo_parse_commit(struct repository *r, struct commit *item)
 {
-       return parse_commit_gently(item, 0);
+       return repo_parse_commit_gently(r, item, 0);
 }
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define parse_commit_internal(item, quiet, use) repo_parse_commit_internal(the_repository, item, quiet, use)
+#define parse_commit_gently(item, quiet) repo_parse_commit_gently(the_repository, item, quiet)
+#define parse_commit(item) repo_parse_commit(the_repository, item)
+#endif
+
 void parse_commit_or_die(struct commit *item);
 
 struct buffer_slab;
@@ -109,7 +118,12 @@ const void *get_cached_commit_buffer(struct repository *, const struct commit *,
  * from disk. The resulting memory should not be modified, and must be given
  * to unuse_commit_buffer when the caller is done.
  */
-const void *get_commit_buffer(const struct commit *, unsigned long *size);
+const void *repo_get_commit_buffer(struct repository *r,
+                                  const struct commit *,
+                                  unsigned long *size);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define get_commit_buffer(c, s) repo_get_commit_buffer(the_repository, c, s)
+#endif
 
 /*
  * Tell the commit subsytem that we are done with a particular commit buffer.
@@ -117,12 +131,17 @@ const void *get_commit_buffer(const struct commit *, unsigned long *size);
  * from an earlier call to get_commit_buffer.  The buffer may or may not be
  * freed by this call; callers should not access the memory afterwards.
  */
-void unuse_commit_buffer(const struct commit *, const void *buffer);
+void repo_unuse_commit_buffer(struct repository *r,
+                             const struct commit *,
+                             const void *buffer);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define unuse_commit_buffer(c, b) repo_unuse_commit_buffer(the_repository, c, b)
+#endif
 
 /*
  * Free any cached object buffer associated with the commit.
  */
-void free_commit_buffer(struct commit *);
+void free_commit_buffer(struct parsed_object_pool *pool, struct commit *);
 
 struct tree *get_commit_tree(const struct commit *);
 struct object_id *get_commit_tree_oid(const struct commit *);
@@ -131,7 +150,7 @@ struct object_id *get_commit_tree_oid(const struct commit *);
  * Release memory related to a commit, including the parent list and
  * any cached object buffer.
  */
-void release_commit_memory(struct commit *c);
+void release_commit_memory(struct parsed_object_pool *pool, struct commit *c);
 
 /*
  * Disassociate any cached object buffer from the commit, but do not free it.
@@ -162,6 +181,14 @@ extern int has_non_ascii(const char *text);
 extern const char *logmsg_reencode(const struct commit *commit,
                                   char **commit_encoding,
                                   const char *output_encoding);
+const char *repo_logmsg_reencode(struct repository *r,
+                                const struct commit *commit,
+                                char **commit_encoding,
+                                const char *output_encoding);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define logmsg_reencode(c, enc, out) repo_logmsg_reencode(the_repository, c, enc, out)
+#endif
+
 extern const char *skip_blank_lines(const char *msg);
 
 /** Removes the first commit from a list sorted by date, and adds all
diff --git a/compat/cygwin.c b/compat/cygwin.c
deleted file mode 100644 (file)
index b9862d6..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "../git-compat-util.h"
-#include "../cache.h"
-
-int cygwin_offset_1st_component(const char *path)
-{
-       const char *pos = path;
-       /* unc paths */
-       if (is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
-               /* skip server name */
-               pos = strchr(pos + 2, '/');
-               if (!pos)
-                       return 0; /* Error: malformed unc path */
-
-               do {
-                       pos++;
-               } while (*pos && pos[0] != '/');
-       }
-       return pos + is_dir_sep(*pos) - path;
-}
diff --git a/compat/cygwin.h b/compat/cygwin.h
deleted file mode 100644 (file)
index 8e52de4..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-int cygwin_offset_1st_component(const char *path);
-#define offset_1st_component cygwin_offset_1st_component
index 34b3880b29d57eee6d6ae0afbb786d7980e7fa3e..0af86840197deb3b0b9d442e3e5236170c31a4de 100644 (file)
@@ -7,6 +7,7 @@
 #include "../cache.h"
 #include "win32/lazyload.h"
 #include "../config.h"
+#include "dir.h"
 
 #define HCAST(type, handle) ((type)(intptr_t)handle)
 
@@ -350,7 +351,7 @@ static inline int needs_hiding(const char *path)
                return 0;
 
        /* We cannot use basename(), as it would remove trailing slashes */
-       mingw_skip_dos_drive_prefix((char **)&path);
+       win32_skip_dos_drive_prefix((char **)&path);
        if (!*path)
                return 0;
 
@@ -1031,7 +1032,7 @@ char *mingw_getcwd(char *pointer, int len)
  * See "Parsing C++ Command-Line Arguments" at Microsoft's Docs:
  * https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
  */
-static const char *quote_arg(const char *arg)
+static const char *quote_arg_msvc(const char *arg)
 {
        /* count chars to quote */
        int len = 0, n = 0;
@@ -1086,6 +1087,37 @@ static const char *quote_arg(const char *arg)
        return q;
 }
 
+#include "quote.h"
+
+static const char *quote_arg_msys2(const char *arg)
+{
+       struct strbuf buf = STRBUF_INIT;
+       const char *p2 = arg, *p;
+
+       for (p = arg; *p; p++) {
+               int ws = isspace(*p);
+               if (!ws && *p != '\\' && *p != '"' && *p != '{')
+                       continue;
+               if (!buf.len)
+                       strbuf_addch(&buf, '"');
+               if (p != p2)
+                       strbuf_add(&buf, p2, p - p2);
+               if (!ws && *p != '{')
+                       strbuf_addch(&buf, '\\');
+               p2 = p;
+       }
+
+       if (p == arg)
+               strbuf_addch(&buf, '"');
+       else if (!buf.len)
+               return arg;
+       else
+               strbuf_add(&buf, p2, p - p2),
+
+       strbuf_addch(&buf, '"');
+       return strbuf_detach(&buf, 0);
+}
+
 static const char *parse_interpreter(const char *cmd)
 {
        static char buf[100];
@@ -1317,6 +1349,47 @@ struct pinfo_t {
 static struct pinfo_t *pinfo = NULL;
 CRITICAL_SECTION pinfo_cs;
 
+/* Used to match and chomp off path components */
+static inline int match_last_path_component(const char *path, size_t *len,
+                                           const char *component)
+{
+       size_t component_len = strlen(component);
+       if (*len < component_len + 1 ||
+           !is_dir_sep(path[*len - component_len - 1]) ||
+           fspathncmp(path + *len - component_len, component, component_len))
+               return 0;
+       *len -= component_len + 1;
+       /* chomp off repeated dir separators */
+       while (*len > 0 && is_dir_sep(path[*len - 1]))
+               (*len)--;
+       return 1;
+}
+
+static int is_msys2_sh(const char *cmd)
+{
+       if (cmd && !strcmp(cmd, "sh")) {
+               static int ret = -1;
+               char *p;
+
+               if (ret >= 0)
+                       return ret;
+
+               p = path_lookup(cmd, 0);
+               if (!p)
+                       ret = 0;
+               else {
+                       size_t len = strlen(p);
+
+                       ret = match_last_path_component(p, &len, "sh.exe") &&
+                               match_last_path_component(p, &len, "bin") &&
+                               match_last_path_component(p, &len, "usr");
+                       free(p);
+               }
+               return ret;
+       }
+       return 0;
+}
+
 static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
                              const char *dir,
                              int prepend_cmd, int fhin, int fhout, int fherr)
@@ -1328,6 +1401,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
        unsigned flags = CREATE_UNICODE_ENVIRONMENT;
        BOOL ret;
        HANDLE cons;
+       const char *(*quote_arg)(const char *arg) =
+               is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;
 
        do_unset_environment_variables();
 
@@ -2275,33 +2350,6 @@ pid_t waitpid(pid_t pid, int *status, int options)
        return -1;
 }
 
-int mingw_skip_dos_drive_prefix(char **path)
-{
-       int ret = has_dos_drive_prefix(*path);
-       *path += ret;
-       return ret;
-}
-
-int mingw_offset_1st_component(const char *path)
-{
-       char *pos = (char *)path;
-
-       /* unc paths */
-       if (!skip_dos_drive_prefix(&pos) &&
-                       is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
-               /* skip server name */
-               pos = strpbrk(pos + 2, "\\/");
-               if (!pos)
-                       return 0; /* Error: malformed unc path */
-
-               do {
-                       pos++;
-               } while (*pos && !is_dir_sep(*pos));
-       }
-
-       return pos + is_dir_sep(*pos) - path;
-}
-
 int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
 {
        int upos = 0, wpos = 0;
index 8c24ddaa3efc20e4454ebc87c51fa30316f64a22..30d9fb3e36274657e5d2a63ef2f5eb3e1c55ce61 100644 (file)
@@ -443,32 +443,12 @@ HANDLE winansi_get_osfhandle(int fd);
  * git specific compatibility
  */
 
-#define has_dos_drive_prefix(path) \
-       (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
-int mingw_skip_dos_drive_prefix(char **path);
-#define skip_dos_drive_prefix mingw_skip_dos_drive_prefix
-static inline int mingw_is_dir_sep(int c)
-{
-       return c == '/' || c == '\\';
-}
-#define is_dir_sep mingw_is_dir_sep
-static inline char *mingw_find_last_dir_sep(const char *path)
-{
-       char *ret = NULL;
-       for (; *path; ++path)
-               if (is_dir_sep(*path))
-                       ret = (char *)path;
-       return ret;
-}
 static inline void convert_slashes(char *path)
 {
        for (; *path; path++)
                if (*path == '\\')
                        *path = '/';
 }
-#define find_last_dir_sep mingw_find_last_dir_sep
-int mingw_offset_1st_component(const char *path);
-#define offset_1st_component mingw_offset_1st_component
 #define PATH_SEP ';'
 extern char *mingw_query_user_email(void);
 #define query_user_email mingw_query_user_email
index 4d1d95beeb509b3bb159b4eaadf20deeb498e73a..27cd5c1ea1f9b3885740b25a161d152b515b43b6 100644 (file)
@@ -112,15 +112,15 @@ compat_symbol (libc, _obstack_compat, _obstack, GLIBC_2_0);
 
 # define CALL_CHUNKFUN(h, size) \
   (((h) -> use_extra_arg) \
-   ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
-   : (*(struct _obstack_chunk *(*) (long)) (h)->chunkfun) ((size)))
+   ? (*(h)->chunkfun.extra) ((h)->extra_arg, (size)) \
+   : (*(h)->chunkfun.plain) ((size)))
 
 # define CALL_FREEFUN(h, old_chunk) \
   do { \
     if ((h) -> use_extra_arg) \
-      (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+      (*(h)->freefun.extra) ((h)->extra_arg, (old_chunk)); \
     else \
-      (*(void (*) (void *)) (h)->freefun) ((old_chunk)); \
+      (*(h)->freefun.plain) ((old_chunk)); \
   } while (0)
 
 \f
@@ -159,8 +159,8 @@ _obstack_begin (struct obstack *h,
       size = 4096 - extra;
     }
 
-  h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;
-  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+  h->chunkfun.plain = chunkfun;
+  h->freefun.plain = freefun;
   h->chunk_size = size;
   h->alignment_mask = alignment - 1;
   h->use_extra_arg = 0;
@@ -206,8 +206,9 @@ _obstack_begin_1 (struct obstack *h, int size, int alignment,
       size = 4096 - extra;
     }
 
-  h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
-  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+  h->chunkfun.extra = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
+  h->freefun.extra = (void (*) (void *, struct _obstack_chunk *)) freefun;
+
   h->chunk_size = size;
   h->alignment_mask = alignment - 1;
   h->extra_arg = arg;
index 6bc24b76445686b2ef55fe5461273e4c5b2cd4f7..ced94d01180a66b2f2d471c1ebaef4eec07e6c49 100644 (file)
@@ -160,11 +160,15 @@ struct obstack            /* control current object in current chunk */
     void *tempptr;
   } temp;                      /* Temporary for some macros.  */
   int   alignment_mask;                /* Mask of alignment for each object. */
-  /* These prototypes vary based on `use_extra_arg', and we use
-     casts to the prototypeless function type in all assignments,
-     but having prototypes here quiets -Wstrict-prototypes.  */
-  struct _obstack_chunk *(*chunkfun) (void *, long);
-  void (*freefun) (void *, struct _obstack_chunk *);
+  /* These prototypes vary based on `use_extra_arg'. */
+  union {
+    void *(*plain) (long);
+    struct _obstack_chunk *(*extra) (void *, long);
+  } chunkfun;
+  union {
+    void (*plain) (void *);
+    void (*extra) (void *, struct _obstack_chunk *);
+  } freefun;
   void *extra_arg;             /* first arg for chunk alloc/dealloc funcs */
   unsigned use_extra_arg:1;    /* chunk alloc/dealloc funcs take extra arg */
   unsigned maybe_empty_object:1;/* There is a possibility that the current
@@ -235,10 +239,10 @@ extern void (*obstack_alloc_failed_handler) (void);
                    (void (*) (void *, void *)) (freefun), (arg))
 
 #define obstack_chunkfun(h, newchunkfun) \
-  ((h) -> chunkfun = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))
+  ((h)->chunkfun.extra = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))
 
 #define obstack_freefun(h, newfreefun) \
-  ((h) -> freefun = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))
+  ((h)->freefun.extra = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))
 
 #define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = (achar))
 
index de61c15d3483fc4a9e8e163bd7cfa5280996c61a..136250fbf6c4f7e707c25dba936e2effebd847f2 100644 (file)
@@ -79,7 +79,7 @@ void precompose_argv(int argc, const char **argv)
                size_t namelen;
                oldarg = argv[i];
                if (has_non_ascii(oldarg, (size_t)-1, &namelen)) {
-                       newarg = reencode_string_iconv(oldarg, namelen, ic_precompose, NULL);
+                       newarg = reencode_string_iconv(oldarg, namelen, ic_precompose, 0, NULL);
                        if (newarg)
                                argv[i] = newarg;
                }
index 51cd60baa37adbeef788098482d804c58cb9979f..c0d838834ad8714cc29148bf23895ab30498947e 100644 (file)
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#if defined __TANDEM
+ /* This is currently duplicated from git-compat-utils.h */
+# ifdef NO_INTPTR_T
+ typedef long intptr_t;
+ typedef unsigned long uintptr_t;
+# endif
+#endif
+
 static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
                                          size_t length, reg_syntax_t syntax);
 static void re_compile_fastmap_iter (regex_t *bufp,
diff --git a/compat/win32/path-utils.c b/compat/win32/path-utils.c
new file mode 100644 (file)
index 0000000..d9d3641
--- /dev/null
@@ -0,0 +1,28 @@
+#include "../../git-compat-util.h"
+
+int win32_skip_dos_drive_prefix(char **path)
+{
+       int ret = has_dos_drive_prefix(*path);
+       *path += ret;
+       return ret;
+}
+
+int win32_offset_1st_component(const char *path)
+{
+       char *pos = (char *)path;
+
+       /* unc paths */
+       if (!skip_dos_drive_prefix(&pos) &&
+                       is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
+               /* skip server name */
+               pos = strpbrk(pos + 2, "\\/");
+               if (!pos)
+                       return 0; /* Error: malformed unc path */
+
+               do {
+                       pos++;
+               } while (*pos && !is_dir_sep(*pos));
+       }
+
+       return pos + is_dir_sep(*pos) - path;
+}
diff --git a/compat/win32/path-utils.h b/compat/win32/path-utils.h
new file mode 100644 (file)
index 0000000..0f70d43
--- /dev/null
@@ -0,0 +1,20 @@
+#define has_dos_drive_prefix(path) \
+       (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
+int win32_skip_dos_drive_prefix(char **path);
+#define skip_dos_drive_prefix win32_skip_dos_drive_prefix
+static inline int win32_is_dir_sep(int c)
+{
+       return c == '/' || c == '\\';
+}
+#define is_dir_sep win32_is_dir_sep
+static inline char *win32_find_last_dir_sep(const char *path)
+{
+       char *ret = NULL;
+       for (; *path; ++path)
+               if (is_dir_sep(*path))
+                       ret = (char *)path;
+       return ret;
+}
+#define find_last_dir_sep win32_find_last_dir_sep
+int win32_offset_1st_component(const char *path);
+#define offset_1st_component win32_offset_1st_component
index bbeeff44fe1e9b4a864cd0dc082906041d65de51..7354fe15b3d93737b1c15c74943da2b7630108d6 100644 (file)
@@ -6,6 +6,7 @@ CFLAGS += -pedantic
 # don't warn for each N_ use
 CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=0
 endif
+CFLAGS += -Wall
 CFLAGS += -Wdeclaration-after-statement
 CFLAGS += -Wformat-security
 CFLAGS += -Wno-format-zero-length
index 3ee7da0e230c4c33e79ae2f3cf498a5cd7cc4881..7b36a1dfe7dc245ab0e9e9d431ee1d7c562915cb 100644 (file)
@@ -187,7 +187,7 @@ ifeq ($(uname_O),Cygwin)
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        MMAP_PREVENTS_DELETE = UnfortunatelyYes
-       COMPAT_OBJS += compat/cygwin.o
+       COMPAT_OBJS += compat/win32/path-utils.o
        FREAD_READS_DIRECTORIES = UnfortunatelyYes
 endif
 ifeq ($(uname_S),FreeBSD)
@@ -233,6 +233,7 @@ ifeq ($(uname_S),OpenBSD)
        HAVE_BSD_SYSCTL = YesPlease
        HAVE_BSD_KERN_PROC_SYSCTL = YesPlease
        PROCFS_EXECUTABLE_PATH = /proc/curproc/file
+       FREAD_READS_DIRECTORIES = UnfortunatelyYes
 endif
 ifeq ($(uname_S),MirBSD)
        NO_STRCASESTR = YesPlease
@@ -441,26 +442,43 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
        # INLINE='' would just replace one set of warnings with another and
        # still not compile in c89 mode, due to non-const array initializations.
        CC = cc -c99
+       # Build down-rev compatible objects that don't use our new getopt_long.
+       ifeq ($(uname_R).$(uname_V),J06.21)
+               CC += -WRVU=J06.20
+       endif
+       ifeq ($(uname_R).$(uname_V),L17.02)
+               CC += -WRVU=L16.05
+       endif
        # Disable all optimization, seems to result in bad code, with -O or -O2
        # or even -O1 (default), /usr/local/libexec/git-core/git-pack-objects
        # abends on "git push". Needs more investigation.
-       CFLAGS = -g -O0
+       CFLAGS = -g -O0 -Winline
        # We'd want it to be here.
        prefix = /usr/local
-       # Our's are in ${prefix}/bin (perl might also be in /usr/bin/perl).
-       PERL_PATH = ${prefix}/bin/perl
-       PYTHON_PATH = ${prefix}/bin/python
-
+       # perl and python must be in /usr/bin on NonStop - supplied by HPE
+       # with operating system in that managed directory.
+       PERL_PATH = /usr/bin/perl
+       PYTHON_PATH = /usr/bin/python
+       # The current /usr/coreutils/rm at lowest support level does not work
+       # with the git test structure. Long paths as in
+       # 'trash directory...' cause rm to terminate prematurely without fully
+       # removing the directory at OS releases J06.21 and L17.02.
+       # Default to the older rm until those two releases are deprecated.
+       RM = /bin/rm -f
        # As detected by './configure'.
        # Missdetected, hence commented out, see below.
        #NO_CURL = YesPlease
        # Added manually, see above.
+       NEEDS_SSL_WITH_CURL = YesPlease
+       NEEDS_CRYPTO_WITH_SSL = YesPlease
+       HAVE_DEV_TTY = YesPlease
        HAVE_LIBCHARSET_H = YesPlease
        HAVE_STRINGS_H = YesPlease
        NEEDS_LIBICONV = YesPlease
        NEEDS_LIBINTL_BEFORE_LIBICONV = YesPlease
        NO_SYS_SELECT_H = UnfortunatelyYes
        NO_D_TYPE_IN_DIRENT = YesPlease
+       NO_GETTEXT = YesPlease
        NO_HSTRERROR = YesPlease
        NO_STRCASESTR = YesPlease
        NO_MEMMEM = YesPlease
@@ -470,7 +488,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
        NO_MKDTEMP = YesPlease
        # Currently libiconv-1.9.1.
        OLD_ICONV = UnfortunatelyYes
-       NO_REGEX = YesPlease
+       NO_REGEX = NeedsStartEnd
        NO_PTHREADS = UnfortunatelyYes
 
        # Not detected (nor checked for) by './configure'.
@@ -527,6 +545,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/winansi.o \
+               compat/win32/path-utils.o \
                compat/win32/pthread.o compat/win32/syslog.o \
                compat/win32/dirent.o
        BASIC_CFLAGS += -DWIN32 -DPROTECT_NTFS_DEFAULT=1
index 24281b608284ee74b262237c467ff054874d8a8e..4813f005ab05279a72ef2894cfae8887965d9640 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -296,7 +296,6 @@ struct ref **get_remote_heads(struct packet_reader *reader,
        struct ref **orig_list = list;
        int len = 0;
        enum get_remote_heads_state state = EXPECTING_FIRST_REF;
-       const char *arg;
 
        *list = NULL;
 
@@ -306,8 +305,6 @@ struct ref **get_remote_heads(struct packet_reader *reader,
                        die_initial_contact(1);
                case PACKET_READ_NORMAL:
                        len = reader->pktlen;
-                       if (len > 4 && skip_prefix(reader->line, "ERR ", &arg))
-                               die(_("remote error: %s"), arg);
                        break;
                case PACKET_READ_FLUSH:
                        state = EXPECTING_DONE;
index 6a7cf3e02da832b3a2829178b968b57f3b80d081..3e536a9834b62353f984f04e0051e20aa5378f48 100644 (file)
@@ -86,36 +86,6 @@ struct object_id OID;
 - hashcmp(OID.hash, OIDPTR->hash)
 + oidcmp(&OID, OIDPTR)
 
-@@
-struct object_id OID1, OID2;
-@@
-- hashcpy(OID1.hash, OID2.hash)
-+ oidcpy(&OID1, &OID2)
-
-@@
-identifier f != oidcpy;
-struct object_id *OIDPTR1;
-struct object_id *OIDPTR2;
-@@
-  f(...) {<...
-- hashcpy(OIDPTR1->hash, OIDPTR2->hash)
-+ oidcpy(OIDPTR1, OIDPTR2)
-  ...>}
-
-@@
-struct object_id *OIDPTR;
-struct object_id OID;
-@@
-- hashcpy(OIDPTR->hash, OID.hash)
-+ oidcpy(OIDPTR, &OID)
-
-@@
-struct object_id *OIDPTR;
-struct object_id OID;
-@@
-- hashcpy(OID.hash, OIDPTR->hash)
-+ oidcpy(&OID, OIDPTR)
-
 @@
 struct object_id *OIDPTR1;
 struct object_id *OIDPTR2;
index e34eada1ad52933230c7fcace595677747f9e1d6..d9ada69b4323f2e9611aa6add5b21a77e622a7c8 100644 (file)
@@ -12,6 +12,36 @@ constant fmt !~ "%";
 )
   );
 
+@@
+expression E;
+struct strbuf SB;
+format F =~ "s";
+@@
+- strbuf_addf(E, "%@F@", SB.buf);
++ strbuf_addbuf(E, &SB);
+
+@@
+expression E;
+struct strbuf *SBP;
+format F =~ "s";
+@@
+- strbuf_addf(E, "%@F@", SBP->buf);
++ strbuf_addbuf(E, SBP);
+
+@@
+expression E;
+struct strbuf SB;
+@@
+- strbuf_addstr(E, SB.buf);
++ strbuf_addbuf(E, &SB);
+
+@@
+expression E;
+struct strbuf *SBP;
+@@
+- strbuf_addstr(E, SBP->buf);
++ strbuf_addbuf(E, SBP);
+
 @@
 expression E1, E2;
 format F =~ "s";
diff --git a/contrib/coccinelle/the_repository.pending.cocci b/contrib/coccinelle/the_repository.pending.cocci
new file mode 100644 (file)
index 0000000..2ee702e
--- /dev/null
@@ -0,0 +1,144 @@
+// This file is used for the ongoing refactoring of
+// bringing the index or repository struct in all of
+// our code base.
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- read_object_file(
++ repo_read_object_file(the_repository,
+  E, F, G)
+
+@@
+expression E;
+@@
+- has_sha1_file(
++ repo_has_sha1_file(the_repository,
+  E)
+
+@@
+expression E;
+expression F;
+@@
+- has_sha1_file_with_flags(
++ repo_has_sha1_file_with_flags(the_repository,
+  E)
+
+@@
+expression E;
+@@
+- has_object_file(
++ repo_has_object_file(the_repository,
+  E)
+
+@@
+expression E;
+expression F;
+@@
+- has_object_file_with_flags(
++ repo_has_object_file_with_flags(the_repository,
+  E)
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- parse_commit_internal(
++ repo_parse_commit_internal(the_repository,
+  E, F, G)
+
+@@
+expression E;
+expression F;
+@@
+- parse_commit_gently(
++ repo_parse_commit_gently(the_repository,
+  E, F)
+
+@@
+expression E;
+@@
+- parse_commit(
++ repo_parse_commit(the_repository,
+  E)
+
+@@
+expression E;
+expression F;
+@@
+- get_merge_bases(
++ repo_get_merge_bases(the_repository,
+  E, F);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- get_merge_bases_many(
++ repo_get_merge_bases_many(the_repository,
+  E, F, G);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- get_merge_bases_many_dirty(
++ repo_get_merge_bases_many_dirty(the_repository,
+  E, F, G);
+
+@@
+expression E;
+expression F;
+@@
+- in_merge_bases(
++ repo_in_merge_bases(the_repository,
+  E, F);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- in_merge_bases_many(
++ repo_in_merge_bases_many(the_repository,
+  E, F, G);
+
+@@
+expression E;
+expression F;
+@@
+- get_commit_buffer(
++ repo_get_commit_buffer(the_repository,
+  E, F);
+
+@@
+expression E;
+expression F;
+@@
+- unuse_commit_buffer(
++ repo_unuse_commit_buffer(the_repository,
+  E, F);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- logmsg_reencode(
++ repo_logmsg_reencode(the_repository,
+  E, F, G);
+
+@@
+expression E;
+expression F;
+expression G;
+expression H;
+@@
+- format_commit_message(
++ repo_format_commit_message(the_repository,
+  E, F, G, H);
index 9e8ec95c3c7adb2956435ded43b020532aafa1c2..499e56f83d0a0592abd53686612c79358780eef2 100644 (file)
@@ -438,7 +438,7 @@ __gitcomp_nl ()
 # Callers must take care of providing only paths that match the current path
 # to be completed and adding any prefix path components, if necessary.
 # 1: List of newline-separated matching paths, complete with all prefix
-#    path componens.
+#    path components.
 __gitcomp_file_direct ()
 {
        local IFS=$'\n'
@@ -855,7 +855,7 @@ __git_compute_merge_strategies ()
 
 __git_complete_revlist_file ()
 {
-       local pfx ls ref cur_="$cur"
+       local dequoted_word pfx ls ref cur_="$cur"
        case "$cur_" in
        *..?*:*)
                return
@@ -863,14 +863,18 @@ __git_complete_revlist_file ()
        ?*:*)
                ref="${cur_%%:*}"
                cur_="${cur_#*:}"
-               case "$cur_" in
+
+               __git_dequote "$cur_"
+
+               case "$dequoted_word" in
                ?*/*)
-                       pfx="${cur_%/*}"
-                       cur_="${cur_##*/}"
+                       pfx="${dequoted_word%/*}"
+                       cur_="${dequoted_word##*/}"
                        ls="$ref:$pfx"
                        pfx="$pfx/"
                        ;;
                *)
+                       cur_="$dequoted_word"
                        ls="$ref"
                        ;;
                esac
@@ -880,21 +884,10 @@ __git_complete_revlist_file ()
                *)   pfx="$ref:$pfx" ;;
                esac
 
-               __gitcomp_nl "$(__git ls-tree "$ls" \
-                               | sed '/^100... blob /{
-                                          s,^.*        ,,
-                                          s,$, ,
-                                      }
-                                      /^120000 blob /{
-                                          s,^.*        ,,
-                                          s,$, ,
-                                      }
-                                      /^040000 tree /{
-                                          s,^.*        ,,
-                                          s,$,/,
-                                      }
-                                      s/^.*    //')" \
-                       "$pfx" "$cur_" ""
+               __gitcomp_file "$(__git ls-tree "$ls" \
+                               | sed 's/^.*    //
+                                      s/$//')" \
+                       "$pfx" "$cur_"
                ;;
        *...*)
                pfx="${cur_%...*}..."
@@ -2993,7 +2986,7 @@ if [[ -n ${ZSH_VERSION-} ]] &&
 
                local IFS=$'\n'
                compset -P '*[=:]'
-               compadd -Q -f -- ${=1} && _ret=0
+               compadd -f -- ${=1} && _ret=0
        }
 
        __gitcomp_file ()
@@ -3002,7 +2995,7 @@ if [[ -n ${ZSH_VERSION-} ]] &&
 
                local IFS=$'\n'
                compset -P '*[=:]'
-               compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
+               compadd -p "${2-}" -f -- ${=1} && _ret=0
        }
 
        _git ()
index 049d6b80f650717f20e68568251c21098f598d80..886bf95d1f5940987f5c4411097fd09b000be037 100644 (file)
@@ -99,7 +99,7 @@ __gitcomp_file_direct ()
 
        local IFS=$'\n'
        compset -P '*[=:]'
-       compadd -Q -f -- ${=1} && _ret=0
+       compadd -f -- ${=1} && _ret=0
 }
 
 __gitcomp_file ()
@@ -108,7 +108,7 @@ __gitcomp_file ()
 
        local IFS=$'\n'
        compset -P '*[=:]'
-       compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
+       compadd -p "${2-}" -f -- ${=1} && _ret=0
 }
 
 __git_zsh_bash_func ()
index 2076cf972b2865dc19cf60dc8df1b69d27041b02..35791fd02c21e6d43accf26ef8f1e4d4a4c1b62f 100644 (file)
@@ -1,3 +1,59 @@
+Release 1.5.0
+=============
+
+Backward-incompatible change
+----------------------------
+
+The name of classes for environment was misnamed as `*Environement`.
+It is now `*Environment`.
+
+New features
+------------
+
+* A Thread-Index header is now added to each email sent (except for
+  combined emails where it would not make sense), so that MS Outlook
+  properly groups messages by threads even though they have a
+  different subject line. Unfortunately, even adding this header the
+  threading still seems to be unreliable, but it is unclear whether
+  this is an issue on our side or on MS Outlook's side (see discussion
+  here: https://github.com/git-multimail/git-multimail/pull/194).
+
+* A new variable multimailhook.ExcludeMergeRevisions was added to send
+  notification emails only for non-merge commits.
+
+* For gitolite environment, it is now possible to specify the mail map
+  in a separate file in addition to gitolite.conf, using the variable
+  multimailhook.MailaddressMap.
+
+Internal changes
+----------------
+
+* The testsuite now uses GIT_PRINT_SHA1_ELLIPSIS where needed for
+  compatibility with recent Git versions. Only tests are affected.
+
+* We don't try to install pyflakes in the continuous integration job
+  for old Python versions where it's no longer available.
+
+* Stop using the deprecated cgi.escape in Python 3.
+
+* New flake8 warnings have been fixed.
+
+* Python 3.6 is now tested against on Travis-CI.
+
+* A bunch of lgtm.com warnings have been fixed.
+
+Bug fixes
+---------
+
+* SMTPMailer logs in only once now. It used to re-login for each email
+  sent which triggered errors for some SMTP servers.
+
+* migrate-mailhook-config was broken by internal refactoring, it
+  should now work again.
+
+This version was tested with Python 2.6 to 3.7. It was tested with Git
+1.7.10.406.gdc801, 2.15.1 and 2.20.1.98.gecbdaf0.
+
 Release 1.4.0
 =============
 
index da65570e9b0d271a12c35dc9d800ba6887323a8f..de20a5428730861268b5065a672cd432e99d0a43 100644 (file)
@@ -4,9 +4,8 @@ Contributing
 git-multimail is an open-source project, built by volunteers. We would
 welcome your help!
 
-The current maintainers are Matthieu Moy
-<matthieu.moy@grenoble-inp.fr> and Michael Haggerty
-<mhagger@alum.mit.edu>.
+The current maintainers are `Matthieu Moy <http://matthieu-moy.fr>`__ and
+`Michael Haggerty <https://github.com/mhagger>`__.
 
 Please note that although a copy of git-multimail is distributed in
 the "contrib" section of the main Git project, development takes place
@@ -33,6 +32,29 @@ mailing list`_.
 Please CC emails regarding git-multimail to the maintainers so that we
 don't overlook them.
 
+Help needed: testers/maintainer for specific environments/OS
+------------------------------------------------------------
+
+The current maintainer uses and tests git-multimail on Linux with the
+Generic environment. More testers, or better contributors are needed
+to test git-multimail on other real-life setups:
+
+* Mac OS X, Windows: git-multimail is currently not supported on these
+  platforms. But since we have no external dependencies and try to
+  write code as portable as possible, it is possible that
+  git-multimail already runs there and if not, it is likely that it
+  could be ported easily.
+
+  Patches to improve support for Windows and OS X are welcome.
+  Ideally, there would be a sub-maintainer for each OS who would test
+  at least once before each release (around twice a year).
+
+* Gerrit, Stash, Gitolite environments: although the testsuite
+  contains tests for these environments, a tester/maintainer for each
+  environment would be welcome to test and report failure (or success)
+  on real-life environments periodically (here also, feedback before
+  each release would be highly appreciated).
+
 
 .. _`git-multimail repository on GitHub`: https://github.com/git-multimail/git-multimail
 .. _`Git mailing list`: git@vger.kernel.org
index 161b0230a05f1c011dbf28fcf1962c8fb3ee37c7..044444245d09e027e71878d11a5437b2b41af27a 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 August 17 2016 and consists of the "git-multimail" subdirectory from
+on January 07 2019 and consists of the "git-multimail" subdirectory from
 revision
 
-    07b1cb6bfd7be156c62e1afa17cae13b850a869f refs/tags/1.4.0
+    04e80e6c40be465cc62b6c246f0fcb8fd2cfd454 refs/tags/1.5.0
 
 Please see the README file in this directory for information about how
 to report bugs or contribute to git-multimail.
similarity index 95%
rename from contrib/hooks/multimail/README
rename to contrib/hooks/multimail/README.rst
index 5105373aea044f2d8fde0c4fd927c8c492d02585..7c0fc4a6ef00362dcff476497a6045a420562d05 100644 (file)
@@ -1,4 +1,4 @@
-git-multimail version 1.4.0
+git-multimail version 1.5.0
 ===========================
 
 .. image:: https://travis-ci.org/git-multimail/git-multimail.svg?branch=master
@@ -20,8 +20,8 @@ GPLv2 (see the COPYING file for details).
 
 Please note: although, as a convenience, git-multimail may be
 distributed along with the main Git project, development of
-git-multimail takes place in its own, separate project.  See section
-"Getting involved" below for more information.
+git-multimail takes place in its own, separate project.  Please, read
+`<CONTRIBUTING.rst>`__ for more information.
 
 
 By default, for each push received by the repository, git-multimail:
@@ -89,6 +89,10 @@ Requirements
   the multimailhook.mailer configuration variable below for how to
   configure git-multimail to send emails via an SMTP server.
 
+* git-multimail is currently tested only on Linux. It may or may not
+  work on other platforms such as Windows and Mac OS. See
+  `<CONTRIBUTING.rst>`__ to improve the situation.
+
 
 Invocation
 ----------
@@ -369,7 +373,7 @@ multimailhook.mailer
         unset, then the value of multimailhook.from is used.
 
       multimailhook.smtpServerTimeout
-        Timeout in seconds.
+        Timeout in seconds. Default is 10.
 
       multimailhook.smtpEncryption
         Set the security type. Allowed values: ``none``, ``ssl``, ``tls`` (starttls).
@@ -419,8 +423,20 @@ multimailhook.from, multimailhook.fromCommit, multimailhook.fromRefchange
     If config values are unset, the value of the From: header is
     determined as follows:
 
-    1. (gitolite environment only) Parse gitolite.conf, looking for a
-       block of comments that looks like this::
+    1. (gitolite environment only)
+       1.a) If ``multimailhook.MailaddressMap`` is set, and is a path
+       to an existing file (if relative, it is considered relative to
+       the place where ``gitolite.conf`` is located), then this file
+       should contain lines like::
+
+           username Firstname Lastname <email@example.com>
+
+       git-multimail will then look for a line where ``$GL_USER``
+       matches the ``username`` part, and use the rest of the line for
+       the ``From:`` header.
+
+       1.b) Parse gitolite.conf, looking for a block of comments that
+       looks like this::
 
            # BEGIN USER EMAILS
            # username Firstname Lastname <email@example.com>
@@ -436,6 +452,11 @@ multimailhook.from, multimailhook.fromCommit, multimailhook.fromRefchange
 
     3. Use the value of multimailhook.envelopeSender.
 
+multimailhook.MailaddressMap
+    (gitolite environment only)
+    File to look for a ``From:`` address based on the user doing the
+    push. Defaults to unset. See ``multimailhook.from`` for details.
+
 multimailhook.administrator
     The name and/or email address of the administrator of the Git
     repository; used in FOOTER_TEMPLATE.  Default is
@@ -484,6 +505,11 @@ multimailhook.maxCommitEmails
     mailbombing, for example on an initial push.  To disable commit
     emails limit, set this option to 0.  The default is 500.
 
+multimailhook.excludeMergeRevisions
+    When sending out revision emails, do not consider merge commits (the
+    functional equivalent of `rev-list --no-merges`).
+    The default is `false` (send merge commit emails).
+
 multimailhook.emailStrictUTF8
     If this boolean option is set to `true`, then the main part of the
     email body is forced to be valid UTF-8.  Any characters that are
index 00aedd9c579f9acc2c94dc8632f2c2b9fa2c96bc..505483310552fa303af4b3533754cd089b537073 100644 (file)
@@ -46,6 +46,15 @@ and add::
       config multimailhook.mailingList = # Where emails should be sent
       config multimailhook.from = # From address to use
 
+Note that by default, gitolite forbids ``<`` and ``>`` in variable
+values (for security/paranoia reasons, see
+`compensating for UNSAFE_PATT
+<http://gitolite.com/gitolite/git-config/index.html#compensating-for-unsafe95patt>`__
+in gitolite's documentation for explanations and a way to disable
+this). As a consequence, you will not be able to use ``First Last
+<First.Last@example.com>`` as recipient email, but specifying
+``First.Last@example.com`` alone works.
+
 Obviously, you can customize all parameters on a per-repository basis by
 adding these ``config multimailhook.*`` lines in the section
 corresponding to a repository or set of repositories.
index 73fdda6b14e331871cf4879ba85906b09cc0add3..8823399e7522e59f9d4f9b9304c1b0ac919cec1f 100755 (executable)
@@ -1,6 +1,6 @@
 #! /usr/bin/env python
 
-__version__ = '1.4.0'
+__version__ = '1.5.0'
 
 # Copyright (c) 2015-2016 Matthieu Moy and others
 # Copyright (c) 2012-2014 Michael Haggerty and others
@@ -64,7 +64,9 @@ except ImportError:
     # Python < 2.6 do not have ssl, but that's OK if we don't use it.
     pass
 import time
-import cgi
+
+import uuid
+import base64
 
 PYTHON3 = sys.version_info >= (3, 0)
 
@@ -73,7 +75,7 @@ if sys.version_info <= (2, 5):
         for element in iterable:
             if not element:
                 return False
-            return True
+        return True
 
 
 def is_ascii(s):
@@ -108,6 +110,12 @@ if PYTHON3:
             return out.decode(sys.getdefaultencoding())
         except UnicodeEncodeError:
             return out.decode(ENCODING)
+
+    import html
+
+    def html_escape(s):
+        return html.escape(s)
+
 else:
     def is_string(s):
         try:
@@ -130,6 +138,10 @@ else:
     def next(it):
         return it.next()
 
+    import cgi
+
+    def html_escape(s):
+        return cgi.escape(s, True)
 
 try:
     from email.charset import Charset
@@ -190,6 +202,7 @@ Content-Transfer-Encoding: 8bit
 Message-ID: %(msgid)s
 From: %(fromaddr)s
 Reply-To: %(reply_to)s
+Thread-Index: %(thread_index)s
 X-Git-Host: %(fqdn)s
 X-Git-Repo: %(repo_shortname)s
 X-Git-Refname: %(refname)s
@@ -322,6 +335,7 @@ From: %(fromaddr)s
 Reply-To: %(reply_to)s
 In-Reply-To: %(reply_to_msgid)s
 References: %(reply_to_msgid)s
+Thread-Index: %(thread_index)s
 X-Git-Host: %(fqdn)s
 X-Git-Repo: %(repo_shortname)s
 X-Git-Refname: %(refname)s
@@ -763,6 +777,9 @@ class GitObject(object):
     def __eq__(self, other):
         return isinstance(other, GitObject) and self.sha1 == other.sha1
 
+    def __ne__(self, other):
+        return not self == other
+
     def __hash__(self):
         return hash(self.sha1)
 
@@ -852,7 +869,7 @@ class Change(object):
         if html_escape_val:
             for k in values:
                 if is_string(values[k]):
-                    values[k] = cgi.escape(values[k], True)
+                    values[k] = html_escape(values[k])
         for line in template.splitlines(True):
             yield line % values
 
@@ -909,7 +926,7 @@ class Change(object):
 
         raise NotImplementedError()
 
-    def generate_email_body(self):
+    def generate_email_body(self, push):
         """Generate the main part of the email body, a line at a time.
 
         The text in the body might be truncated after a specified
@@ -936,7 +953,7 @@ class Change(object):
             yield "<pre style='margin:0'>\n"
 
             for line in lines:
-                yield cgi.escape(line)
+                yield html_escape(line)
 
             yield '</pre>\n'
         else:
@@ -1011,7 +1028,7 @@ class Change(object):
                     fgcolor = '404040'
 
                 # Chop the trailing LF, we don't want it inside <pre>.
-                line = cgi.escape(line[:-1])
+                line = html_escape(line[:-1])
 
                 if bgcolor or fgcolor:
                     style = 'display:block; white-space:pre;'
@@ -1060,6 +1077,10 @@ class Revision(Change):
         self.author = read_git_output(['log', '--no-walk', '--format=%aN <%aE>', self.rev.sha1])
         self.recipients = self.environment.get_revision_recipients(self)
 
+        # -s is short for --no-patch, but -s works on older git's (e.g. 1.7)
+        self.parents = read_git_lines(['show', '-s', '--format=%P',
+                                      self.rev.sha1])[0].split()
+
         self.cc_recipients = ''
         if self.environment.get_scancommitforcc():
             self.cc_recipients = ', '.join(to.strip() for to in self._cc_recipients())
@@ -1090,6 +1111,7 @@ class Revision(Change):
             oneline = oneline[:max_subject_length - 6] + ' [...]'
 
         values['rev'] = self.rev.sha1
+        values['parents'] = ' '.join(self.parents)
         values['rev_short'] = self.rev.short
         values['change_type'] = self.change_type
         values['refname'] = self.refname
@@ -1097,6 +1119,7 @@ class Revision(Change):
         values['short_refname'] = self.reference_change.short_refname
         values['refname_type'] = self.reference_change.refname_type
         values['reply_to_msgid'] = self.reference_change.msgid
+        values['thread_index'] = self.reference_change.thread_index
         values['num'] = self.num
         values['tot'] = self.tot
         values['recipients'] = self.recipients
@@ -1244,6 +1267,23 @@ class ReferenceChange(Change):
             old=old, new=new, rev=rev,
             )
 
+    @staticmethod
+    def make_thread_index():
+        """Return a string appropriate for the Thread-Index header,
+        needed by MS Outlook to get threading right.
+
+        The format is (base64-encoded):
+        - 1 byte must be 1
+        - 5 bytes encode a date (hardcoded here)
+        - 16 bytes for a globally unique identifier
+
+        FIXME: Unfortunately, even with the Thread-Index field, MS
+        Outlook doesn't seem to do the threading reliably (see
+        https://github.com/git-multimail/git-multimail/pull/194).
+        """
+        thread_index = b'\x01\x00\x00\x12\x34\x56' + uuid.uuid4().bytes
+        return base64.standard_b64encode(thread_index).decode('ascii')
+
     def __init__(self, environment, refname, short_refname, old, new, rev):
         Change.__init__(self, environment)
         self.change_type = {
@@ -1257,6 +1297,7 @@ class ReferenceChange(Change):
         self.new = new
         self.rev = rev
         self.msgid = make_msgid()
+        self.thread_index = self.make_thread_index()
         self.diffopts = environment.diffopts
         self.graphopts = environment.graphopts
         self.logopts = environment.logopts
@@ -1276,6 +1317,7 @@ class ReferenceChange(Change):
         values['refname'] = self.refname
         values['short_refname'] = self.short_refname
         values['msgid'] = self.msgid
+        values['thread_index'] = self.thread_index
         values['recipients'] = self.recipients
         values['oldrev'] = str(self.old)
         values['oldrev_short'] = self.old.short
@@ -1941,6 +1983,9 @@ class Mailer(object):
     def __init__(self, environment):
         self.environment = environment
 
+    def close(self):
+        pass
+
     def send(self, lines, to_addrs):
         """Send an email consisting of lines.
 
@@ -2054,6 +2099,7 @@ class SMTPMailer(Mailer):
         self.username = smtpuser
         self.password = smtppass
         self.smtpcacerts = smtpcacerts
+        self.loggedin = False
         try:
             def call(klass, server, timeout):
                 try:
@@ -2130,20 +2176,30 @@ class SMTPMailer(Mailer):
                 % (self.smtpserver, sys.exc_info()[1]))
             sys.exit(1)
 
-    def __del__(self):
+    def close(self):
         if hasattr(self, 'smtp'):
             self.smtp.quit()
             del self.smtp
 
+    def __del__(self):
+        self.close()
+
     def send(self, lines, to_addrs):
         try:
             if self.username or self.password:
-                self.smtp.login(self.username, self.password)
+                if not self.loggedin:
+                    self.smtp.login(self.username, self.password)
+                    self.loggedin = True
             msg = ''.join(lines)
             # turn comma-separated list into Python list if needed.
             if is_string(to_addrs):
                 to_addrs = [email for (name, email) in getaddresses([to_addrs])]
             self.smtp.sendmail(self.envelopesender, to_addrs, msg)
+        except socket.timeout:
+            self.environment.get_logger().error(
+                '*** Error sending email ***\n'
+                '*** SMTP server timed out (timeout is %s)\n'
+                % self.smtpservertimeout)
         except smtplib.SMTPResponseException:
             err = sys.exc_info()[1]
             self.environment.get_logger().error(
@@ -2171,7 +2227,8 @@ class OutputMailer(Mailer):
 
     SEPARATOR = '=' * 75 + '\n'
 
-    def __init__(self, f):
+    def __init__(self, f, environment=None):
+        super(OutputMailer, self).__init__(environment=environment)
         self.f = f
 
     def send(self, lines, to_addrs):
@@ -2382,6 +2439,7 @@ class Environment(object):
         self.html_in_footer = False
         self.commitBrowseURL = None
         self.maxcommitemails = 500
+        self.excludemergerevisions = False
         self.diffopts = ['--stat', '--summary', '--find-copies-harder']
         self.graphopts = ['--oneline', '--decorate']
         self.logopts = []
@@ -2621,6 +2679,8 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin):
 
         self.commitBrowseURL = config.get('commitBrowseURL')
 
+        self.excludemergerevisions = config.get('excludeMergeRevisions')
+
         maxcommitemails = config.get('maxcommitemails')
         if maxcommitemails is not None:
             try:
@@ -3152,7 +3212,10 @@ class GitoliteEnvironmentHighPrecMixin(Environment):
         return self.osenv.get('GL_USER', 'unknown user')
 
 
-class GitoliteEnvironmentLowPrecMixin(Environment):
+class GitoliteEnvironmentLowPrecMixin(
+        ConfigEnvironmentMixin,
+        Environment):
+
     def get_repo_shortname(self):
         # The gitolite environment variable $GL_REPO is a pretty good
         # repo_shortname (though it's probably not as good as a value
@@ -3162,6 +3225,16 @@ class GitoliteEnvironmentLowPrecMixin(Environment):
             super(GitoliteEnvironmentLowPrecMixin, self).get_repo_shortname()
             )
 
+    @staticmethod
+    def _compile_regex(re_template):
+        return (
+            re.compile(re_template % x)
+            for x in (
+                r'BEGIN\s+USER\s+EMAILS',
+                r'([^\s]+)\s+(.*)',
+                r'END\s+USER\s+EMAILS',
+                ))
+
     def get_fromaddr(self, change=None):
         GL_USER = self.osenv.get('GL_USER')
         if GL_USER is not None:
@@ -3174,18 +3247,42 @@ class GitoliteEnvironmentLowPrecMixin(Environment):
             GL_CONF = self.osenv.get(
                 'GL_CONF',
                 os.path.join(GL_ADMINDIR, 'conf', 'gitolite.conf'))
+
+            mailaddress_map = self.config.get('MailaddressMap')
+            # If relative, consider relative to GL_CONF:
+            if mailaddress_map:
+                mailaddress_map = os.path.join(os.path.dirname(GL_CONF),
+                                               mailaddress_map)
+                if os.path.isfile(mailaddress_map):
+                    f = open(mailaddress_map, 'rU')
+                    try:
+                        # Leading '#' is optional
+                        re_begin, re_user, re_end = self._compile_regex(
+                            r'^(?:\s*#)?\s*%s\s*$')
+                        for l in f:
+                            l = l.rstrip('\n')
+                            if re_begin.match(l) or re_end.match(l):
+                                continue  # Ignore these lines
+                            m = re_user.match(l)
+                            if m:
+                                if m.group(1) == GL_USER:
+                                    return m.group(2)
+                                else:
+                                    continue  # Not this user, but not an error
+                            raise ConfigurationException(
+                                "Syntax error in mail address map.\n"
+                                "Check file {}.\n"
+                                "Line: {}".format(mailaddress_map, l))
+
+                    finally:
+                        f.close()
+
             if os.path.isfile(GL_CONF):
                 f = open(GL_CONF, 'rU')
                 try:
                     in_user_emails_section = False
-                    re_template = r'^\s*#\s*%s\s*$'
-                    re_begin, re_user, re_end = (
-                        re.compile(re_template % x)
-                        for x in (
-                            r'BEGIN\s+USER\s+EMAILS',
-                            re.escape(GL_USER) + r'\s+(.*)',
-                            r'END\s+USER\s+EMAILS',
-                            ))
+                    re_begin, re_user, re_end = self._compile_regex(
+                        r'^\s*#\s*%s\s*$')
                     for l in f:
                         l = l.rstrip('\n')
                         if not in_user_emails_section:
@@ -3195,8 +3292,8 @@ class GitoliteEnvironmentLowPrecMixin(Environment):
                         if re_end.match(l):
                             break
                         m = re_user.match(l)
-                        if m:
-                            return m.group(1)
+                        if m and m.group(1) == GL_USER:
+                            return m.group(2)
                 finally:
                     f.close()
         return super(GitoliteEnvironmentLowPrecMixin, self).get_fromaddr(change)
@@ -3228,7 +3325,7 @@ class StashEnvironmentHighPrecMixin(Environment):
         self.__repo = repo
 
     def get_pusher(self):
-        return re.match('(.*?)\s*<', self.__user).group(1)
+        return re.match(r'(.*?)\s*<', self.__user).group(1)
 
     def get_pusher_email(self):
         return self.__user
@@ -3262,7 +3359,7 @@ class GerritEnvironmentHighPrecMixin(Environment):
             if self.__submitter.find('<') != -1:
                 # Submitter has a configured email, we transformed
                 # __submitter into an RFC 2822 string already.
-                return re.match('(.*?)\s*<', self.__submitter).group(1)
+                return re.match(r'(.*?)\s*<', self.__submitter).group(1)
             else:
                 # Submitter has no configured email, it's just his name.
                 return self.__submitter
@@ -3615,6 +3712,9 @@ class Push(object):
 
             for (num, sha1) in enumerate(sha1s):
                 rev = Revision(change, GitObject(sha1), num=num + 1, tot=len(sha1s))
+                if len(rev.parents) > 1 and change.environment.excludemergerevisions:
+                    # skipping a merge commit
+                    continue
                 if not rev.recipients and rev.cc_recipients:
                     change.environment.log_msg('*** Replacing Cc: with To:')
                     rev.recipients = rev.cc_recipients
@@ -3664,11 +3764,14 @@ def run_as_post_receive_hook(environment, mailer):
         changes.append(
             ReferenceChange.create(environment, oldrev, newrev, refname)
             )
-    if changes:
-        push = Push(environment, changes)
+    if not changes:
+        mailer.close()
+        return
+    push = Push(environment, changes)
+    try:
         push.send_emails(mailer, body_filter=environment.filter_body)
-    if hasattr(mailer, '__del__'):
-        mailer.__del__()
+    finally:
+        mailer.close()
 
 
 def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send=False):
@@ -3687,10 +3790,14 @@ def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send=
             refname,
             ),
         ]
+    if not changes:
+        mailer.close()
+        return
     push = Push(environment, changes, force_send)
-    push.send_emails(mailer, body_filter=environment.filter_body)
-    if hasattr(mailer, '__del__'):
-        mailer.__del__()
+    try:
+        push.send_emails(mailer, body_filter=environment.filter_body)
+    finally:
+        mailer.close()
 
 
 def check_ref_filter(environment):
@@ -3860,7 +3967,7 @@ def build_environment_klass(env_name):
         low_prec_mixin = known_env['lowprec']
         environment_mixins.append(low_prec_mixin)
     environment_mixins.append(Environment)
-    klass_name = env_name.capitalize() + 'Environement'
+    klass_name = env_name.capitalize() + 'Environment'
     environment_klass = type(
         klass_name,
         tuple(environment_mixins),
@@ -4057,21 +4164,21 @@ class Logger(object):
                 environment, 'git_multimail.error', environment.error_log_file, logging.ERROR)
             self.loggers.append(error_log_file)
 
-    def info(self, msg):
+    def info(self, msg, *args, **kwargs):
         for l in self.loggers:
-            l.info(msg)
+            l.info(msg, *args, **kwargs)
 
-    def debug(self, msg):
+    def debug(self, msg, *args, **kwargs):
         for l in self.loggers:
-            l.debug(msg)
+            l.debug(msg, *args, **kwargs)
 
-    def warning(self, msg):
+    def warning(self, msg, *args, **kwargs):
         for l in self.loggers:
-            l.warning(msg)
+            l.warning(msg, *args, **kwargs)
 
-    def error(self, msg):
+    def error(self, msg, *args, **kwargs):
         for l in self.loggers:
-            l.error(msg)
+            l.error(msg, *args, **kwargs)
 
 
 def main(args):
@@ -4189,7 +4296,7 @@ def main(args):
             show_env(environment, sys.stderr)
 
         if options.stdout or environment.stdout:
-            mailer = OutputMailer(sys.stdout)
+            mailer = OutputMailer(sys.stdout, environment)
         else:
             mailer = choose_mailer(config, environment)
 
@@ -4234,5 +4341,6 @@ def main(args):
             sys.stderr.write(msg)
         sys.exit(1)
 
+
 if __name__ == '__main__':
     main(sys.argv[1:])
index 992657bbdc3545070496fbb43a287679cba03ca1..241ba22fa3c88f004798559a986cc51934b5d4f9 100755 (executable)
@@ -110,11 +110,12 @@ def is_section_empty(section, local):
 
     try:
         read_output(
-            ['git', 'config']
-            + local_option
-            ['--get-regexp', '^%s\.' % (section,)]
+            ['git', 'config'] +
+            local_option +
+            ['--get-regexp', '^%s\.' % (section,)]
             )
-    except CommandError, e:
+    except CommandError:
+        t, e, traceback = sys.exc_info()
         if e.retcode == 1:
             # This means that no settings were found.
             return True
@@ -188,7 +189,9 @@ def migrate_config(strict=False, retain=False, overwrite=False):
             sys.stderr.write(
                 '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
                 )
-            new.set_recipients(name, old.get_recipients(name))
+            old_recipients = old.get_all(name, default=None)
+            old_recipients = ', '.join(o.strip() for o in old_recipients)
+            new.set_recipients(name, old_recipients)
 
     if strict:
         sys.stderr.write(
index 1ea113d274e27adfede78bd5b5530608a47fa378..b9bb11834e1f2f0160ed6b56c4b6cd363006c731 100755 (executable)
@@ -30,7 +30,6 @@ script's behavior could be changed or customized.
 """
 
 import sys
-import os
 
 # If necessary, add the path to the directory containing
 # git_multimail.py to the Python path as follows.  (This is not
@@ -86,6 +85,7 @@ mailer = git_multimail.choose_mailer(config, environment)
 
 # Use Python's smtplib to send emails.  Both arguments are required.
 #mailer = git_multimail.SMTPMailer(
+#    environment=environment,
 #    envelopesender='git-repo@example.com',
 #    # The smtpserver argument can also include a port number; e.g.,
 #    #     smtpserver='mail.example.com:25'
index e0848226d21bbbeb26d9ac6217fe1e61ccc71fca..0d89ae7c2302a4bff3a6fdcb4a7015a81eeb8025 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -705,7 +705,7 @@ static int filter_buffer_or_fd(int in, int out, void *data)
 }
 
 static int apply_single_file_filter(const char *path, const char *src, size_t len, int fd,
-                        struct strbuf *dst, const char *cmd)
+                                   struct strbuf *dst, const char *cmd)
 {
        /*
         * Create a pipeline to have the command filter the buffer's
@@ -778,7 +778,8 @@ static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
 
 static void handle_filter_error(const struct strbuf *filter_status,
                                struct cmd2process *entry,
-                               const unsigned int wanted_capability) {
+                               const unsigned int wanted_capability)
+{
        if (!strcmp(filter_status->buf, "error"))
                ; /* The filter signaled a problem with the file. */
        else if (!strcmp(filter_status->buf, "abort") && wanted_capability) {
@@ -1091,7 +1092,7 @@ static int count_ident(const char *cp, unsigned long size)
 }
 
 static int ident_to_git(const char *path, const char *src, size_t len,
-                        struct strbuf *buf, int ident)
+                       struct strbuf *buf, int ident)
 {
        char *dst, *dollar;
 
@@ -1135,7 +1136,7 @@ static int ident_to_git(const char *path, const char *src, size_t len,
 }
 
 static int ident_to_worktree(const char *path, const char *src, size_t len,
-                             struct strbuf *buf, int ident)
+                            struct strbuf *buf, int ident)
 {
        struct object_id oid;
        char *to_free = NULL, *dollar, *spc;
index 4dfbc8c9f917a600c0995b7e33927df3fa19453d..ec1271f89ce08b3b117fe09715f61730c7943bea 100644 (file)
@@ -91,7 +91,8 @@ static timestamp_t check_expirations(void)
 }
 
 static int read_request(FILE *fh, struct credential *c,
-                       struct strbuf *action, int *timeout) {
+                       struct strbuf *action, int *timeout)
+{
        static struct strbuf item = STRBUF_INIT;
        const char *p;
 
index 8e5018e40614f3af617440fb0a2ee2f4b9e1d32c..2186bd0738ed2fcbe216cf24a6c99ed4dc4ccd9b 100644 (file)
@@ -190,13 +190,15 @@ static void set_island_marks(struct object *obj, struct island_bitmap *marks)
        island_bitmap_or(b, marks);
 }
 
-static void mark_remote_island_1(struct remote_island *rl, int is_core_island)
+static void mark_remote_island_1(struct repository *r,
+                                struct remote_island *rl,
+                                int is_core_island)
 {
        uint32_t i;
 
        for (i = 0; i < rl->oids.nr; ++i) {
                struct island_bitmap *marks;
-               struct object *obj = parse_object(the_repository, &rl->oids.oid[i]);
+               struct object *obj = parse_object(r, &rl->oids.oid[i]);
 
                if (!obj)
                        continue;
@@ -211,7 +213,7 @@ static void mark_remote_island_1(struct remote_island *rl, int is_core_island)
                while (obj && obj->type == OBJ_TAG) {
                        obj = ((struct tag *)obj)->tagged;
                        if (obj) {
-                               parse_object(the_repository, &obj->oid);
+                               parse_object(r, &obj->oid);
                                marks = create_or_get_island_marks(obj);
                                island_bitmap_set(marks, island_counter);
                        }
@@ -237,7 +239,9 @@ static int tree_depth_compare(const void *a, const void *b)
        return todo_a->depth - todo_b->depth;
 }
 
-void resolve_tree_islands(int progress, struct packing_data *to_pack)
+void resolve_tree_islands(struct repository *r,
+                         int progress,
+                         struct packing_data *to_pack)
 {
        struct progress *progress_state = NULL;
        struct tree_islands_todo *todo;
@@ -281,7 +285,7 @@ void resolve_tree_islands(int progress, struct packing_data *to_pack)
 
                root_marks = kh_value(island_marks, pos);
 
-               tree = lookup_tree(the_repository, &ent->idx.oid);
+               tree = lookup_tree(r, &ent->idx.oid);
                if (!tree || parse_tree(tree) < 0)
                        die(_("bad tree object %s"), oid_to_hex(&ent->idx.oid));
 
@@ -292,7 +296,7 @@ void resolve_tree_islands(int progress, struct packing_data *to_pack)
                        if (S_ISGITLINK(entry.mode))
                                continue;
 
-                       obj = lookup_object(the_repository, entry.oid->hash);
+                       obj = lookup_object(r, entry.oid.hash);
                        if (!obj)
                                continue;
 
@@ -415,7 +419,7 @@ static struct remote_island *get_core_island(void)
        return NULL;
 }
 
-static void deduplicate_islands(void)
+static void deduplicate_islands(struct repository *r)
 {
        struct remote_island *island, *core = NULL, **list;
        unsigned int island_count, dst, src, ref, i = 0;
@@ -444,20 +448,20 @@ static void deduplicate_islands(void)
        core = get_core_island();
 
        for (i = 0; i < island_count; ++i) {
-               mark_remote_island_1(list[i], core && list[i]->hash == core->hash);
+               mark_remote_island_1(r, list[i], core && list[i]->hash == core->hash);
        }
 
        free(list);
 }
 
-void load_delta_islands(void)
+void load_delta_islands(struct repository *r)
 {
        island_marks = kh_init_sha1();
        remote_islands = kh_init_str();
 
        git_config(island_config_callback, NULL);
        for_each_ref(find_island_for_ref, NULL);
-       deduplicate_islands();
+       deduplicate_islands(r);
 
        fprintf(stderr, _("Marked %d islands, done.\n"), island_counter);
 }
index b635cd07d8d80ea14b86e72da5dcaee1fb4ab11a..3ac8045d8c528be81ac1cbefe9942c534e3c498a 100644 (file)
@@ -1,14 +1,17 @@
 #ifndef DELTA_ISLANDS_H
 #define DELTA_ISLANDS_H
 
+struct commit;
 struct object_id;
 struct packing_data;
-struct commit;
+struct repository;
 
 int island_delta_cmp(const struct object_id *a, const struct object_id *b);
 int in_same_island(const struct object_id *, const struct object_id *);
-void resolve_tree_islands(int progress, struct packing_data *to_pack);
-void load_delta_islands(void);
+void resolve_tree_islands(struct repository *r,
+                         int progress,
+                         struct packing_data *to_pack);
+void load_delta_islands(struct repository *r);
 void propagate_island_marks(struct commit *commit);
 int compute_pack_layers(struct packing_data *to_pack);
 
index 83fce51518854754ee6f4d95cca86d61cc10e4cf..23c8d351b3aa5912c49c9da0eecd8727cf06ac72 100644 (file)
@@ -542,7 +542,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 {
        struct rev_info revs;
 
-       repo_init_revisions(the_repository, &revs, NULL);
+       repo_init_revisions(opt->repo, &revs, NULL);
        copy_pathspec(&revs.prune_data, &opt->pathspec);
        revs.diffopt = *opt;
 
@@ -551,13 +551,14 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
        return 0;
 }
 
-int index_differs_from(const char *def, const struct diff_flags *flags,
+int index_differs_from(struct repository *r,
+                      const char *def, const struct diff_flags *flags,
                       int ita_invisible_in_index)
 {
        struct rev_info rev;
        struct setup_revision_opt opt;
 
-       repo_init_revisions(the_repository, &rev, NULL);
+       repo_init_revisions(r, &rev, NULL);
        memset(&opt, 0, sizeof(opt));
        opt.def = def;
        setup_revisions(0, NULL, &rev, &opt);
diff --git a/diff.c b/diff.c
index dc9965e836cd49c46a01293ef1d678a619b8f997..5306c48652db59e84c26383d68cf4a7d896647d4 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -291,7 +291,7 @@ static int parse_color_moved(const char *arg)
                return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed-zebra', 'plain'"));
 }
 
-static int parse_color_moved_ws(const char *arg)
+static unsigned parse_color_moved_ws(const char *arg)
 {
        int ret = 0;
        struct string_list l = STRING_LIST_INIT_DUP;
@@ -304,7 +304,9 @@ static int parse_color_moved_ws(const char *arg)
                strbuf_addstr(&sb, i->string);
                strbuf_trim(&sb);
 
-               if (!strcmp(sb.buf, "ignore-space-change"))
+               if (!strcmp(sb.buf, "no"))
+                       ret = 0;
+               else if (!strcmp(sb.buf, "ignore-space-change"))
                        ret |= XDF_IGNORE_WHITESPACE_CHANGE;
                else if (!strcmp(sb.buf, "ignore-space-at-eol"))
                        ret |= XDF_IGNORE_WHITESPACE_AT_EOL;
@@ -312,15 +314,19 @@ static int parse_color_moved_ws(const char *arg)
                        ret |= XDF_IGNORE_WHITESPACE;
                else if (!strcmp(sb.buf, "allow-indentation-change"))
                        ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE;
-               else
-                       error(_("ignoring unknown color-moved-ws mode '%s'"), sb.buf);
+               else {
+                       ret |= COLOR_MOVED_WS_ERROR;
+                       error(_("unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', 'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"), sb.buf);
+               }
 
                strbuf_release(&sb);
        }
 
        if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) &&
-           (ret & XDF_WHITESPACE_FLAGS))
-               die(_("color-moved-ws: allow-indentation-change cannot be combined with other white space modes"));
+           (ret & XDF_WHITESPACE_FLAGS)) {
+               error(_("color-moved-ws: allow-indentation-change cannot be combined with other whitespace modes"));
+               ret |= COLOR_MOVED_WS_ERROR;
+       }
 
        string_list_clear(&l, 0);
 
@@ -341,8 +347,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                return 0;
        }
        if (!strcmp(var, "diff.colormovedws")) {
-               int cm = parse_color_moved_ws(value);
-               if (cm < 0)
+               unsigned cm = parse_color_moved_ws(value);
+               if (cm & COLOR_MOVED_WS_ERROR)
                        return -1;
                diff_color_moved_ws_default = cm;
                return 0;
@@ -489,7 +495,7 @@ static const char *external_diff(void)
 
        if (done_preparing)
                return external_diff_cmd;
-       external_diff_cmd = getenv("GIT_EXTERNAL_DIFF");
+       external_diff_cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF"));
        if (!external_diff_cmd)
                external_diff_cmd = external_diff_cmd_cfg;
        done_preparing = 1;
@@ -750,6 +756,8 @@ struct emitted_diff_symbol {
        const char *line;
        int len;
        int flags;
+       int indent_off;   /* Offset to first non-whitespace character */
+       int indent_width; /* The visual width of the indentation */
        enum diff_symbol s;
 };
 #define EMITTED_DIFF_SYMBOL_INIT {NULL}
@@ -780,44 +788,85 @@ struct moved_entry {
        struct moved_entry *next_line;
 };
 
-/**
- * The struct ws_delta holds white space differences between moved lines, i.e.
- * between '+' and '-' lines that have been detected to be a move.
- * The string contains the difference in leading white spaces, before the
- * rest of the line is compared using the white space config for move
- * coloring. The current_longer indicates if the first string in the
- * comparision is longer than the second.
- */
-struct ws_delta {
-       char *string;
-       unsigned int current_longer : 1;
-};
-#define WS_DELTA_INIT { NULL, 0 }
-
 struct moved_block {
        struct moved_entry *match;
-       struct ws_delta wsd;
+       int wsd; /* The whitespace delta of this block */
 };
 
 static void moved_block_clear(struct moved_block *b)
 {
-       FREE_AND_NULL(b->wsd.string);
-       b->match = NULL;
+       memset(b, 0, sizeof(*b));
 }
 
-static int compute_ws_delta(const struct emitted_diff_symbol *a,
-                            const struct emitted_diff_symbol *b,
-                            struct ws_delta *out)
+#define INDENT_BLANKLINE INT_MIN
+
+static void fill_es_indent_data(struct emitted_diff_symbol *es)
 {
-       const struct emitted_diff_symbol *longer =  a->len > b->len ? a : b;
-       const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a;
-       int d = longer->len - shorter->len;
+       unsigned int off = 0, i;
+       int width = 0, tab_width = es->flags & WS_TAB_WIDTH_MASK;
+       const char *s = es->line;
+       const int len = es->len;
+
+       /* skip any \v \f \r at start of indentation */
+       while (s[off] == '\f' || s[off] == '\v' ||
+              (s[off] == '\r' && off < len - 1))
+               off++;
+
+       /* calculate the visual width of indentation */
+       while(1) {
+               if (s[off] == ' ') {
+                       width++;
+                       off++;
+               } else if (s[off] == '\t') {
+                       width += tab_width - (width % tab_width);
+                       while (s[++off] == '\t')
+                               width += tab_width;
+               } else {
+                       break;
+               }
+       }
+
+       /* check if this line is blank */
+       for (i = off; i < len; i++)
+               if (!isspace(s[i]))
+                   break;
+
+       if (i == len) {
+               es->indent_width = INDENT_BLANKLINE;
+               es->indent_off = len;
+       } else {
+               es->indent_off = off;
+               es->indent_width = width;
+       }
+}
 
-       if (strncmp(longer->line + d, shorter->line, shorter->len))
+static int compute_ws_delta(const struct emitted_diff_symbol *a,
+                           const struct emitted_diff_symbol *b,
+                           int *out)
+{
+       int a_len = a->len,
+           b_len = b->len,
+           a_off = a->indent_off,
+           a_width = a->indent_width,
+           b_off = b->indent_off,
+           b_width = b->indent_width;
+       int delta;
+
+       if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE) {
+               *out = INDENT_BLANKLINE;
+               return 1;
+       }
+
+       if (a->s == DIFF_SYMBOL_PLUS)
+               delta = a_width - b_width;
+       else
+               delta = b_width - a_width;
+
+       if (a_len - a_off != b_len - b_off ||
+           memcmp(a->line + a_off, b->line + b_off, a_len - a_off))
                return 0;
 
-       out->string = xmemdupz(longer->line, d);
-       out->current_longer = (a == longer);
+       *out = delta;
 
        return 1;
 }
@@ -829,51 +878,53 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
                                 int n)
 {
        struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
-       int al = cur->es->len, cl = l->len;
+       int al = cur->es->len, bl = match->es->len, cl = l->len;
        const char *a = cur->es->line,
                   *b = match->es->line,
                   *c = l->line;
-
-       int wslen;
+       int a_off = cur->es->indent_off,
+           a_width = cur->es->indent_width,
+           c_off = l->indent_off,
+           c_width = l->indent_width;
+       int delta;
 
        /*
-        * We need to check if 'cur' is equal to 'match'.
-        * As those are from the same (+/-) side, we do not need to adjust for
-        * indent changes. However these were found using fuzzy matching
-        * so we do have to check if they are equal.
+        * We need to check if 'cur' is equal to 'match'.  As those
+        * are from the same (+/-) side, we do not need to adjust for
+        * indent changes. However these were found using fuzzy
+        * matching so we do have to check if they are equal. Here we
+        * just check the lengths. We delay calling memcmp() to check
+        * the contents until later as if the length comparison for a
+        * and c fails we can avoid the call all together.
         */
-       if (strcmp(a, b))
+       if (al != bl)
                return 1;
 
-       if (!pmb->wsd.string)
-               /*
-                * The white space delta is not active? This can happen
-                * when we exit early in this function.
-                */
-               return 1;
+       /* If 'l' and 'cur' are both blank then they match. */
+       if (a_width == INDENT_BLANKLINE && c_width == INDENT_BLANKLINE)
+               return 0;
 
        /*
-        * The indent changes of the block are known and stored in
-        * pmb->wsd; however we need to check if the indent changes of the
-        * current line are still the same as before.
-        *
-        * To do so we need to compare 'l' to 'cur', adjusting the
-        * one of them for the white spaces, depending which was longer.
+        * The indent changes of the block are known and stored in pmb->wsd;
+        * however we need to check if the indent changes of the current line
+        * match those of the current block and that the text of 'l' and 'cur'
+        * after the indentation match.
         */
+       if (cur->es->s == DIFF_SYMBOL_PLUS)
+               delta = a_width - c_width;
+       else
+               delta = c_width - a_width;
 
-       wslen = strlen(pmb->wsd.string);
-       if (pmb->wsd.current_longer) {
-               c += wslen;
-               cl -= wslen;
-       } else {
-               a += wslen;
-               al -= wslen;
-       }
-
-       if (al != cl || memcmp(a, c, al))
-               return 1;
+       /*
+        * If the previous lines of this block were all blank then set its
+        * whitespace delta.
+        */
+       if (pmb->wsd == INDENT_BLANKLINE)
+               pmb->wsd = delta;
 
-       return 0;
+       return !(delta == pmb->wsd && al - a_off == cl - c_off &&
+                !memcmp(a, b, al) && !
+                memcmp(a + a_off, c + c_off, al - a_off));
 }
 
 static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
@@ -939,6 +990,9 @@ static void add_lines_to_move_detection(struct diff_options *o,
                        continue;
                }
 
+               if (o->color_moved_ws_handling &
+                   COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+                       fill_es_indent_data(&o->emitted_symbols->buf[n]);
                key = prepare_entry(o, n);
                if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
                        prev_line->next_line = key;
@@ -1017,8 +1071,7 @@ static int shrink_potential_moved_blocks(struct moved_block *pmb,
 
                if (lp < pmb_nr && rp > -1 && lp < rp) {
                        pmb[lp] = pmb[rp];
-                       pmb[rp].match = NULL;
-                       pmb[rp].wsd.string = NULL;
+                       memset(&pmb[rp], 0, sizeof(pmb[rp]));
                        rp--;
                        lp++;
                }
@@ -1038,14 +1091,17 @@ static int shrink_potential_moved_blocks(struct moved_block *pmb,
  * The last block consists of the (n - block_length)'th line up to but not
  * including the nth line.
  *
+ * Returns 0 if the last block is empty or is unset by this function, non zero
+ * otherwise.
+ *
  * NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
  * Think of a way to unify them.
  */
-static void adjust_last_block(struct diff_options *o, int n, int block_length)
+static int adjust_last_block(struct diff_options *o, int n, int block_length)
 {
        int i, alnum_count = 0;
        if (o->color_moved == COLOR_MOVED_PLAIN)
-               return;
+               return block_length;
        for (i = 1; i < block_length + 1; i++) {
                const char *c = o->emitted_symbols->buf[n - i].line;
                for (; *c; c++) {
@@ -1053,11 +1109,12 @@ static void adjust_last_block(struct diff_options *o, int n, int block_length)
                                continue;
                        alnum_count++;
                        if (alnum_count >= COLOR_MOVED_MIN_ALNUM_COUNT)
-                               return;
+                               return 1;
                }
        }
        for (i = 1; i < block_length + 1; i++)
                o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
+       return 0;
 }
 
 /* Find blocks of moved code, delegate actual coloring decision to helper */
@@ -1067,7 +1124,7 @@ static void mark_color_as_moved(struct diff_options *o,
 {
        struct moved_block *pmb = NULL; /* potentially moved blocks */
        int pmb_nr = 0, pmb_alloc = 0;
-       int n, flipped_block = 1, block_length = 0;
+       int n, flipped_block = 0, block_length = 0;
 
 
        for (n = 0; n < o->emitted_symbols->nr; n++) {
@@ -1075,6 +1132,7 @@ static void mark_color_as_moved(struct diff_options *o,
                struct moved_entry *key;
                struct moved_entry *match = NULL;
                struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+               enum diff_symbol last_symbol = 0;
 
                switch (l->s) {
                case DIFF_SYMBOL_PLUS:
@@ -1090,7 +1148,7 @@ static void mark_color_as_moved(struct diff_options *o,
                        free(key);
                        break;
                default:
-                       flipped_block = 1;
+                       flipped_block = 0;
                }
 
                if (!match) {
@@ -1101,13 +1159,16 @@ static void mark_color_as_moved(struct diff_options *o,
                                moved_block_clear(&pmb[i]);
                        pmb_nr = 0;
                        block_length = 0;
+                       flipped_block = 0;
+                       last_symbol = l->s;
                        continue;
                }
 
-               l->flags |= DIFF_SYMBOL_MOVED_LINE;
-
-               if (o->color_moved == COLOR_MOVED_PLAIN)
+               if (o->color_moved == COLOR_MOVED_PLAIN) {
+                       last_symbol = l->s;
+                       l->flags |= DIFF_SYMBOL_MOVED_LINE;
                        continue;
+               }
 
                if (o->color_moved_ws_handling &
                    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
@@ -1130,21 +1191,27 @@ static void mark_color_as_moved(struct diff_options *o,
                                                             &pmb[pmb_nr].wsd))
                                                pmb[pmb_nr++].match = match;
                                } else {
-                                       pmb[pmb_nr].wsd.string = NULL;
+                                       pmb[pmb_nr].wsd = 0;
                                        pmb[pmb_nr++].match = match;
                                }
                        }
 
-                       flipped_block = (flipped_block + 1) % 2;
+                       if (adjust_last_block(o, n, block_length) &&
+                           pmb_nr && last_symbol != l->s)
+                               flipped_block = (flipped_block + 1) % 2;
+                       else
+                               flipped_block = 0;
 
-                       adjust_last_block(o, n, block_length);
                        block_length = 0;
                }
 
-               block_length++;
-
-               if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
-                       l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
+               if (pmb_nr) {
+                       block_length++;
+                       l->flags |= DIFF_SYMBOL_MOVED_LINE;
+                       if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
+                               l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
+               }
+               last_symbol = l->s;
        }
        adjust_last_block(o, n, block_length);
 
@@ -1488,7 +1555,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
 static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
                             const char *line, int len, unsigned flags)
 {
-       struct emitted_diff_symbol e = {line, len, flags, s};
+       struct emitted_diff_symbol e = {line, len, flags, 0, 0, s};
 
        if (o->emitted_symbols)
                append_emitted_diff_symbol(o, &e);
@@ -1637,7 +1704,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
        strbuf_release(&msgbuf);
 }
 
-static struct diff_tempfile *claim_diff_tempfile(void) {
+static struct diff_tempfile *claim_diff_tempfile(void)
+{
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
                if (!diff_temp[i].name)
@@ -3313,14 +3381,14 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
                options->b_prefix = b;
 }
 
-struct userdiff_driver *get_textconv(struct index_state *istate,
+struct userdiff_driver *get_textconv(struct repository *r,
                                     struct diff_filespec *one)
 {
        if (!DIFF_FILE_VALID(one))
                return NULL;
 
-       diff_filespec_load_driver(one, istate);
-       return userdiff_get_textconv(one->driver);
+       diff_filespec_load_driver(one, r->index);
+       return userdiff_get_textconv(r, one->driver);
 }
 
 static void builtin_diff(const char *name_a,
@@ -3369,8 +3437,8 @@ static void builtin_diff(const char *name_a,
        }
 
        if (o->flags.allow_textconv) {
-               textconv_one = get_textconv(o->repo->index, one);
-               textconv_two = get_textconv(o->repo->index, two);
+               textconv_one = get_textconv(o->repo, one);
+               textconv_two = get_textconv(o->repo, two);
        }
 
        /* Never use a non-valid filename anywhere if at all possible */
@@ -3476,7 +3544,7 @@ static void builtin_diff(const char *name_a,
                o->found_changes = 1;
        } else {
                /* Crazy xdl interfaces.. */
-               const char *diffopts = getenv("GIT_DIFF_OPTS");
+               const char *diffopts;
                const char *v;
                xpparam_t xpp;
                xdemitconf_t xecfg;
@@ -3519,12 +3587,15 @@ static void builtin_diff(const char *name_a,
                        xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
                if (pe)
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
+
+               diffopts = getenv("GIT_DIFF_OPTS");
                if (!diffopts)
                        ;
                else if (skip_prefix(diffopts, "--unified=", &v))
                        xecfg.ctxlen = strtoul(v, NULL, 10);
                else if (skip_prefix(diffopts, "-u", &v))
                        xecfg.ctxlen = strtoul(v, NULL, 10);
+
                if (o->word_diff)
                        init_diff_words_data(&ecbdata, o, one, two);
                if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
@@ -4819,7 +4890,8 @@ static int parse_diff_filter_opt(const char *optarg, struct diff_options *opt)
        return 0;
 }
 
-static void enable_patch_output(int *fmt) {
+static void enable_patch_output(int *fmt)
+{
        *fmt &= ~DIFF_FORMAT_NO_OUTPUT;
        *fmt |= DIFF_FORMAT_PATCH;
 }
@@ -5034,10 +5106,15 @@ int diff_opt_parse(struct diff_options *options,
        else if (skip_prefix(arg, "--color-moved=", &arg)) {
                int cm = parse_color_moved(arg);
                if (cm < 0)
-                       die("bad --color-moved argument: %s", arg);
+                       return error("bad --color-moved argument: %s", arg);
                options->color_moved = cm;
+       } else if (!strcmp(arg, "--no-color-moved-ws")) {
+               options->color_moved_ws_handling = 0;
        } else if (skip_prefix(arg, "--color-moved-ws=", &arg)) {
-               options->color_moved_ws_handling = parse_color_moved_ws(arg);
+               unsigned cm = parse_color_moved_ws(arg);
+               if (cm & COLOR_MOVED_WS_ERROR)
+                       return -1;
+               options->color_moved_ws_handling = cm;
        } else if (skip_to_optional_arg_default(arg, "--color-words", &options->word_regex, NULL)) {
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
@@ -5885,8 +5962,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
 
                for (i = 0; i < esm.nr; i++)
                        free((void *)esm.buf[i].line);
+               esm.nr = 0;
+
+               o->emitted_symbols = NULL;
        }
-       esm.nr = 0;
 }
 
 void diff_flush(struct diff_options *options)
@@ -6434,7 +6513,7 @@ int textconv_object(struct repository *r,
 
        df = alloc_filespec(path);
        fill_filespec(df, oid, oid_valid, mode);
-       textconv = get_textconv(r->index, df);
+       textconv = get_textconv(r, df);
        if (!textconv) {
                free_filespec(df);
                return 0;
diff --git a/diff.h b/diff.h
index ce5e8a8183e848b2e36d50bea271f687a450e3af..b512d0477ac3a4a0338094a4d3b21770ecb57dd8 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -225,7 +225,8 @@ struct diff_options {
 
        /* XDF_WHITESPACE_FLAGS regarding block detection are set at 2, 3, 4 */
        #define COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE (1<<5)
-       int color_moved_ws_handling;
+       #define COLOR_MOVED_WS_ERROR (1<<0)
+       unsigned color_moved_ws_handling;
 
        struct repository *repo;
 };
@@ -436,7 +437,8 @@ int diff_result_code(struct diff_options *, int);
 
 void diff_no_index(struct repository *, struct rev_info *, int, const char **);
 
-int index_differs_from(const char *def, const struct diff_flags *flags,
+int index_differs_from(struct repository *r, const char *def,
+                      const struct diff_flags *flags,
                       int ita_invisible_in_index);
 
 /*
@@ -460,7 +462,7 @@ size_t fill_textconv(struct repository *r,
  * and only if it has textconv enabled (otherwise return NULL). The result
  * can be passed to fill_textconv().
  */
-struct userdiff_driver *get_textconv(struct index_state *istate,
+struct userdiff_driver *get_textconv(struct repository *r,
                                     struct diff_filespec *one);
 
 /*
index 69fc55ea1e34af69b7ed4d57e521cb4825f99fd2..a9c6d60df22862e47ccb7fc1c7de9ed7cc4b1236 100644 (file)
@@ -140,8 +140,8 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
                return 0;
 
        if (o->flags.allow_textconv) {
-               textconv_one = get_textconv(o->repo->index, p->one);
-               textconv_two = get_textconv(o->repo->index, p->two);
+               textconv_one = get_textconv(o->repo, p->one);
+               textconv_two = get_textconv(o->repo, p->two);
        }
 
        /*
@@ -154,6 +154,12 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
        if (textconv_one == textconv_two && diff_unmodified_pair(p))
                return 0;
 
+       if ((o->pickaxe_opts & DIFF_PICKAXE_KIND_G) &&
+           !o->flags.text &&
+           ((!textconv_one && diff_filespec_is_binary(o->repo, p->one)) ||
+            (!textconv_two && diff_filespec_is_binary(o->repo, p->two))))
+               return 0;
+
        mf1.size = fill_textconv(o->repo, textconv_one, p->one, &mf1.ptr);
        mf2.size = fill_textconv(o->repo, textconv_two, p->two, &mf2.ptr);
 
diff --git a/dir.c b/dir.c
index ab6477d777e96cb9789f1c6b3220af349731a701..21284482198147687fb03892265f97ac5ed58d18 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -276,44 +276,6 @@ static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
 #define DO_MATCH_DIRECTORY (1<<1)
 #define DO_MATCH_SUBMODULE (1<<2)
 
-static int match_attrs(const struct index_state *istate,
-                      const char *name, int namelen,
-                      const struct pathspec_item *item)
-{
-       int i;
-       char *to_free = NULL;
-
-       if (name[namelen])
-               name = to_free = xmemdupz(name, namelen);
-
-       git_check_attr(istate, name, item->attr_check);
-
-       free(to_free);
-
-       for (i = 0; i < item->attr_match_nr; i++) {
-               const char *value;
-               int matched;
-               enum attr_match_mode match_mode;
-
-               value = item->attr_check->items[i].value;
-               match_mode = item->attr_match[i].match_mode;
-
-               if (ATTR_TRUE(value))
-                       matched = (match_mode == MATCH_SET);
-               else if (ATTR_FALSE(value))
-                       matched = (match_mode == MATCH_UNSET);
-               else if (ATTR_UNSET(value))
-                       matched = (match_mode == MATCH_UNSPECIFIED);
-               else
-                       matched = (match_mode == MATCH_VALUE &&
-                                  !strcmp(item->attr_match[i].value, value));
-               if (!matched)
-                       return 0;
-       }
-
-       return 1;
-}
-
 /*
  * Does 'match' match the given name?
  * A match is found if
@@ -367,7 +329,8 @@ static int match_pathspec_item(const struct index_state *istate,
            strncmp(item->match, name - prefix, item->prefix))
                return 0;
 
-       if (item->attr_match_nr && !match_attrs(istate, name, namelen, item))
+       if (item->attr_match_nr &&
+           !match_pathspec_attrs(istate, name, namelen, item))
                return 0;
 
        /* If the match was just the prefix, we matched */
diff --git a/entry.c b/entry.c
index 0a3c451f5f0f08deabe20b4ca1858825f2b29f3f..6fd72b30c8768677f044923ef5e715949d32fdae 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -161,7 +161,7 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
        return !available;
 }
 
-int finish_delayed_checkout(struct checkout *state)
+int finish_delayed_checkout(struct checkout *state, int *nr_checkouts)
 {
        int errs = 0;
        unsigned delayed_object_count;
@@ -226,7 +226,7 @@ int finish_delayed_checkout(struct checkout *state)
                                ce = index_file_exists(state->istate, path->string,
                                                       strlen(path->string), 0);
                                if (ce) {
-                                       errs |= checkout_entry(ce, state, NULL);
+                                       errs |= checkout_entry(ce, state, NULL, nr_checkouts);
                                        filtered_bytes += ce->ce_stat_data.sd_size;
                                        display_throughput(progress, filtered_bytes);
                                } else
@@ -435,8 +435,8 @@ static void mark_colliding_entries(const struct checkout *state,
  * its name is returned in topath[], which must be able to hold at
  * least TEMPORARY_FILENAME_LENGTH bytes long.
  */
-int checkout_entry(struct cache_entry *ce,
-                  const struct checkout *state, char *topath)
+int checkout_entry(struct cache_entry *ce, const struct checkout *state,
+                  char *topath, int *nr_checkouts)
 {
        static struct strbuf path = STRBUF_INIT;
        struct stat st;
@@ -506,5 +506,7 @@ int checkout_entry(struct cache_entry *ce,
                return 0;
 
        create_directories(path.buf, path.len, state);
+       if (nr_checkouts)
+               (*nr_checkouts)++;
        return write_entry(ce, path.buf, state, 0);
 }
index 346559770773e923766c06593ada84c72815061f..89af47cb8504903b90460d5faa91be9f61bd2fc3 100644 (file)
@@ -107,7 +107,7 @@ char *git_work_tree_cfg;
 
 static char *git_namespace;
 
-static const char *super_prefix;
+static char *super_prefix;
 
 /*
  * Repository-local GIT_* environment variables; see cache.h for details.
@@ -240,7 +240,7 @@ const char *get_super_prefix(void)
 {
        static int initialized;
        if (!initialized) {
-               super_prefix = getenv(GIT_SUPER_PREFIX_ENVIRONMENT);
+               super_prefix = xstrdup_or_null(getenv(GIT_SUPER_PREFIX_ENVIRONMENT));
                initialized = 1;
        }
        return super_prefix;
@@ -274,9 +274,9 @@ const char *get_git_work_tree(void)
 
 char *get_object_directory(void)
 {
-       if (!the_repository->objects->objectdir)
+       if (!the_repository->objects->odb)
                BUG("git environment hasn't been setup");
-       return the_repository->objects->objectdir;
+       return the_repository->objects->odb->path;
 }
 
 int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)
index 69886687ce95fd7dfe5363d0c776e21241b6b830..b7ba755c2b88df35d5e50272d7b9341134625c56 100644 (file)
@@ -1,157 +1,3 @@
-/*
-(See Documentation/git-fast-import.txt for maintained documentation.)
-Format of STDIN stream:
-
-  stream ::= cmd*;
-
-  cmd ::= new_blob
-        | new_commit
-        | new_tag
-        | reset_branch
-        | checkpoint
-        | progress
-        ;
-
-  new_blob ::= 'blob' lf
-    mark?
-    file_content;
-  file_content ::= data;
-
-  new_commit ::= 'commit' sp ref_str lf
-    mark?
-    ('author' (sp name)? sp '<' email '>' sp when lf)?
-    'committer' (sp name)? sp '<' email '>' sp when lf
-    commit_msg
-    ('from' sp commit-ish lf)?
-    ('merge' sp commit-ish lf)*
-    (file_change | ls)*
-    lf?;
-  commit_msg ::= data;
-
-  ls ::= 'ls' sp '"' quoted(path) '"' lf;
-
-  file_change ::= file_clr
-    | file_del
-    | file_rnm
-    | file_cpy
-    | file_obm
-    | file_inm;
-  file_clr ::= 'deleteall' lf;
-  file_del ::= 'D' sp path_str lf;
-  file_rnm ::= 'R' sp path_str sp path_str lf;
-  file_cpy ::= 'C' sp path_str sp path_str lf;
-  file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
-  file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
-    data;
-  note_obm ::= 'N' sp (hexsha1 | idnum) sp commit-ish lf;
-  note_inm ::= 'N' sp 'inline' sp commit-ish lf
-    data;
-
-  new_tag ::= 'tag' sp tag_str lf
-    'from' sp commit-ish lf
-    ('tagger' (sp name)? sp '<' email '>' sp when lf)?
-    tag_msg;
-  tag_msg ::= data;
-
-  reset_branch ::= 'reset' sp ref_str lf
-    ('from' sp commit-ish lf)?
-    lf?;
-
-  checkpoint ::= 'checkpoint' lf
-    lf?;
-
-  progress ::= 'progress' sp not_lf* lf
-    lf?;
-
-     # note: the first idnum in a stream should be 1 and subsequent
-     # idnums should not have gaps between values as this will cause
-     # the stream parser to reserve space for the gapped values.  An
-     # idnum can be updated in the future to a new object by issuing
-     # a new mark directive with the old idnum.
-     #
-  mark ::= 'mark' sp idnum lf;
-  data ::= (delimited_data | exact_data)
-    lf?;
-
-    # note: delim may be any string but must not contain lf.
-    # data_line may contain any data but must not be exactly
-    # delim.
-  delimited_data ::= 'data' sp '<<' delim lf
-    (data_line lf)*
-    delim lf;
-
-     # note: declen indicates the length of binary_data in bytes.
-     # declen does not include the lf preceding the binary data.
-     #
-  exact_data ::= 'data' sp declen lf
-    binary_data;
-
-     # note: quoted strings are C-style quoting supporting \c for
-     # common escapes of 'c' (e..g \n, \t, \\, \") or \nnn where nnn
-     # is the signed byte value in octal.  Note that the only
-     # characters which must actually be escaped to protect the
-     # stream formatting is: \, " and LF.  Otherwise these values
-     # are UTF8.
-     #
-  commit-ish  ::= (ref_str | hexsha1 | sha1exp_str | idnum);
-  ref_str     ::= ref;
-  sha1exp_str ::= sha1exp;
-  tag_str     ::= tag;
-  path_str    ::= path    | '"' quoted(path)    '"' ;
-  mode        ::= '100644' | '644'
-                | '100755' | '755'
-                | '120000'
-                ;
-
-  declen ::= # unsigned 32 bit value, ascii base10 notation;
-  bigint ::= # unsigned integer value, ascii base10 notation;
-  binary_data ::= # file content, not interpreted;
-
-  when         ::= raw_when | rfc2822_when;
-  raw_when     ::= ts sp tz;
-  rfc2822_when ::= # Valid RFC 2822 date and time;
-
-  sp ::= # ASCII space character;
-  lf ::= # ASCII newline (LF) character;
-
-     # note: a colon (':') must precede the numerical value assigned to
-     # an idnum.  This is to distinguish it from a ref or tag name as
-     # GIT does not permit ':' in ref or tag strings.
-     #
-  idnum   ::= ':' bigint;
-  path    ::= # GIT style file path, e.g. "a/b/c";
-  ref     ::= # GIT ref name, e.g. "refs/heads/MOZ_GECKO_EXPERIMENT";
-  tag     ::= # GIT tag name, e.g. "FIREFOX_1_5";
-  sha1exp ::= # Any valid GIT SHA1 expression;
-  hexsha1 ::= # SHA1 in hexadecimal format;
-
-     # note: name and email are UTF8 strings, however name must not
-     # contain '<' or lf and email must not contain any of the
-     # following: '<', '>', lf.
-     #
-  name  ::= # valid GIT author/committer name;
-  email ::= # valid GIT author/committer email;
-  ts    ::= # time since the epoch in seconds, ascii base10 notation;
-  tz    ::= # GIT style timezone;
-
-     # note: comments, get-mark, ls-tree, and cat-blob requests may
-     # appear anywhere in the input, except within a data command. Any
-     # form of the data command always escapes the related input from
-     # comment processing.
-     #
-     # In case it is not clear, the '#' that starts the comment
-     # must be the first character on that line (an lf
-     # preceded it).
-     #
-
-  get_mark ::= 'get-mark' sp idnum lf;
-  cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
-  ls_tree  ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;
-
-  comment ::= '#' not_lf* lf;
-  not_lf  ::= # Any byte that is not ASCII newline (LF);
-*/
-
 #include "builtin.h"
 #include "cache.h"
 #include "repository.h"
@@ -1968,6 +1814,13 @@ static void parse_mark(void)
                next_mark = 0;
 }
 
+static void parse_original_identifier(void)
+{
+       const char *v;
+       if (skip_prefix(command_buf.buf, "original-oid ", &v))
+               read_next_command();
+}
+
 static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
 {
        const char *data;
@@ -2110,6 +1963,7 @@ static void parse_new_blob(void)
 {
        read_next_command();
        parse_mark();
+       parse_original_identifier();
        parse_and_store_blob(&last_blob, NULL, next_mark);
 }
 
@@ -2733,6 +2587,7 @@ static void parse_new_commit(const char *arg)
 
        read_next_command();
        parse_mark();
+       parse_original_identifier();
        if (skip_prefix(command_buf.buf, "author ", &v)) {
                author = parse_ident(v);
                read_next_command();
@@ -2865,6 +2720,9 @@ static void parse_new_tag(const char *arg)
                die("Invalid ref name or SHA1 expression: %s", from);
        read_next_command();
 
+       /* original-oid ... */
+       parse_original_identifier();
+
        /* tagger ... */
        if (skip_prefix(command_buf.buf, "tagger ", &v)) {
                tagger = parse_ident(v);
index 9691046e6445837beefde3d8ae5e842063842575..08b3b356faeabdeeff90918142f7225ce5460e8c 100644 (file)
@@ -135,38 +135,42 @@ enum ack_type {
        ACK_ready
 };
 
-static void consume_shallow_list(struct fetch_pack_args *args, int fd)
+static void consume_shallow_list(struct fetch_pack_args *args,
+                                struct packet_reader *reader)
 {
        if (args->stateless_rpc && args->deepen) {
                /* If we sent a depth we will get back "duplicate"
                 * shallow and unshallow commands every time there
                 * is a block of have lines exchanged.
                 */
-               char *line;
-               while ((line = packet_read_line(fd, NULL))) {
-                       if (starts_with(line, "shallow "))
+               while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
+                       if (starts_with(reader->line, "shallow "))
                                continue;
-                       if (starts_with(line, "unshallow "))
+                       if (starts_with(reader->line, "unshallow "))
                                continue;
                        die(_("git fetch-pack: expected shallow list"));
                }
+               if (reader->status != PACKET_READ_FLUSH)
+                       die(_("git fetch-pack: expected a flush packet after shallow list"));
        }
 }
 
-static enum ack_type get_ack(int fd, struct object_id *result_oid)
+static enum ack_type get_ack(struct packet_reader *reader,
+                            struct object_id *result_oid)
 {
        int len;
-       char *line = packet_read_line(fd, &len);
        const char *arg;
 
-       if (!line)
+       if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                die(_("git fetch-pack: expected ACK/NAK, got a flush packet"));
-       if (!strcmp(line, "NAK"))
+       len = reader->pktlen;
+
+       if (!strcmp(reader->line, "NAK"))
                return NAK;
-       if (skip_prefix(line, "ACK ", &arg)) {
+       if (skip_prefix(reader->line, "ACK ", &arg)) {
                if (!get_oid_hex(arg, result_oid)) {
                        arg += 40;
-                       len -= arg - line;
+                       len -= arg - reader->line;
                        if (len < 1)
                                return ACK;
                        if (strstr(arg, "continue"))
@@ -178,9 +182,7 @@ static enum ack_type get_ack(int fd, struct object_id *result_oid)
                        return ACK;
                }
        }
-       if (skip_prefix(line, "ERR ", &arg))
-               die(_("remote error: %s"), arg);
-       die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line);
+       die(_("git fetch-pack: expected ACK/NAK, got '%s'"), reader->line);
 }
 
 static void send_request(struct fetch_pack_args *args,
@@ -248,10 +250,15 @@ static int find_common(struct fetch_negotiator *negotiator,
        int got_ready = 0;
        struct strbuf req_buf = STRBUF_INIT;
        size_t state_len = 0;
+       struct packet_reader reader;
 
        if (args->stateless_rpc && multi_ack == 1)
                die(_("--stateless-rpc requires multi_ack_detailed"));
 
+       packet_reader_init(&reader, fd[0], NULL, 0,
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
+
        if (!args->no_dependents) {
                mark_tips(negotiator, args->negotiation_tips);
                for_each_cached_alternate(negotiator, insert_one_alternate_object);
@@ -329,38 +336,42 @@ static int find_common(struct fetch_negotiator *negotiator,
                        packet_buf_write(&req_buf, "deepen-not %s", s->string);
                }
        }
-       if (server_supports_filtering && args->filter_options.choice)
+       if (server_supports_filtering && args->filter_options.choice) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
+               expand_list_objects_filter_spec(&args->filter_options,
+                                               &expanded_filter_spec);
                packet_buf_write(&req_buf, "filter %s",
-                                args->filter_options.filter_spec);
+                                expanded_filter_spec.buf);
+               strbuf_release(&expanded_filter_spec);
+       }
        packet_buf_flush(&req_buf);
        state_len = req_buf.len;
 
        if (args->deepen) {
-               char *line;
                const char *arg;
                struct object_id oid;
 
                send_request(args, fd[1], &req_buf);
-               while ((line = packet_read_line(fd[0], NULL))) {
-                       if (skip_prefix(line, "shallow ", &arg)) {
+               while (packet_reader_read(&reader) == PACKET_READ_NORMAL) {
+                       if (skip_prefix(reader.line, "shallow ", &arg)) {
                                if (get_oid_hex(arg, &oid))
-                                       die(_("invalid shallow line: %s"), line);
+                                       die(_("invalid shallow line: %s"), reader.line);
                                register_shallow(the_repository, &oid);
                                continue;
                        }
-                       if (skip_prefix(line, "unshallow ", &arg)) {
+                       if (skip_prefix(reader.line, "unshallow ", &arg)) {
                                if (get_oid_hex(arg, &oid))
-                                       die(_("invalid unshallow line: %s"), line);
+                                       die(_("invalid unshallow line: %s"), reader.line);
                                if (!lookup_object(the_repository, oid.hash))
-                                       die(_("object not found: %s"), line);
+                                       die(_("object not found: %s"), reader.line);
                                /* make sure that it is parsed as shallow */
                                if (!parse_object(the_repository, &oid))
-                                       die(_("error in object: %s"), line);
+                                       die(_("error in object: %s"), reader.line);
                                if (unregister_shallow(&oid))
-                                       die(_("no shallow found: %s"), line);
+                                       die(_("no shallow found: %s"), reader.line);
                                continue;
                        }
-                       die(_("expected shallow/unshallow, got %s"), line);
+                       die(_("expected shallow/unshallow, got %s"), reader.line);
                }
        } else if (!args->stateless_rpc)
                send_request(args, fd[1], &req_buf);
@@ -397,9 +408,9 @@ static int find_common(struct fetch_negotiator *negotiator,
                        if (!args->stateless_rpc && count == INITIAL_FLUSH)
                                continue;
 
-                       consume_shallow_list(args, fd[0]);
+                       consume_shallow_list(args, &reader);
                        do {
-                               ack = get_ack(fd[0], result_oid);
+                               ack = get_ack(&reader, result_oid);
                                if (ack)
                                        print_verbose(args, _("got %s %d %s"), "ack",
                                                      ack, oid_to_hex(result_oid));
@@ -469,9 +480,9 @@ done:
        strbuf_release(&req_buf);
 
        if (!got_ready || !no_done)
-               consume_shallow_list(args, fd[0]);
+               consume_shallow_list(args, &reader);
        while (flushes || multi_ack) {
-               int ack = get_ack(fd[0], result_oid);
+               int ack = get_ack(&reader, result_oid);
                if (ack) {
                        print_verbose(args, _("got %s (%d) %s"), "ack",
                                      ack, oid_to_hex(result_oid));
@@ -636,23 +647,6 @@ struct loose_object_iter {
        struct ref *refs;
 };
 
-/*
- *  If the number of refs is not larger than the number of loose objects,
- *  this function stops inserting.
- */
-static int add_loose_objects_to_set(const struct object_id *oid,
-                                   const char *path,
-                                   void *data)
-{
-       struct loose_object_iter *iter = data;
-       oidset_insert(iter->loose_object_set, oid);
-       if (iter->refs == NULL)
-               return 1;
-
-       iter->refs = iter->refs->next;
-       return 0;
-}
-
 /*
  * Mark recent commits available locally and reachable from a local ref as
  * COMPLETE. If args->no_dependents is false, also mark COMPLETE remote refs as
@@ -670,30 +664,14 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
        struct ref *ref;
        int old_save_commit_buffer = save_commit_buffer;
        timestamp_t cutoff = 0;
-       struct oidset loose_oid_set = OIDSET_INIT;
-       int use_oidset = 0;
-       struct loose_object_iter iter = {&loose_oid_set, *refs};
-
-       /* Enumerate all loose objects or know refs are not so many. */
-       use_oidset = !for_each_loose_object(add_loose_objects_to_set,
-                                           &iter, 0);
 
        save_commit_buffer = 0;
 
        for (ref = *refs; ref; ref = ref->next) {
                struct object *o;
-               unsigned int flags = OBJECT_INFO_QUICK;
 
-               if (use_oidset &&
-                   !oidset_contains(&loose_oid_set, &ref->old_oid)) {
-                       /*
-                        * I know this does not exist in the loose form,
-                        * so check if it exists in a non-loose form.
-                        */
-                       flags |= OBJECT_INFO_IGNORE_LOOSE;
-               }
-
-               if (!has_object_file_with_flags(&ref->old_oid, flags))
+               if (!has_object_file_with_flags(&ref->old_oid,
+                                               OBJECT_INFO_QUICK))
                        continue;
                o = parse_object(the_repository, &ref->old_oid);
                if (!o)
@@ -710,8 +688,6 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
                }
        }
 
-       oidset_clear(&loose_oid_set);
-
        if (!args->deepen) {
                for_each_ref(mark_complete_oid, NULL);
                for_each_cached_alternate(NULL, mark_alternate_complete);
@@ -1042,6 +1018,8 @@ static void add_shallow_requests(struct strbuf *req_buf,
                        packet_buf_write(req_buf, "deepen-not %s", s->string);
                }
        }
+       if (args->deepen_relative)
+               packet_buf_write(req_buf, "deepen-relative\n");
 }
 
 static void add_wants(int no_dependents, const struct ref *wants, struct strbuf *req_buf)
@@ -1119,7 +1097,8 @@ static int add_haves(struct fetch_negotiator *negotiator,
 static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
                              const struct fetch_pack_args *args,
                              const struct ref *wants, struct oidset *common,
-                             int *haves_to_send, int *in_vain)
+                             int *haves_to_send, int *in_vain,
+                             int sideband_all)
 {
        int ret = 0;
        struct strbuf req_buf = STRBUF_INIT;
@@ -1145,6 +1124,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
                packet_buf_write(&req_buf, "include-tag");
        if (prefer_ofs_delta)
                packet_buf_write(&req_buf, "ofs-delta");
+       if (sideband_all)
+               packet_buf_write(&req_buf, "sideband-all");
 
        /* Add shallow-info and deepen request */
        if (server_supports_feature("fetch", "shallow", 0))
@@ -1155,9 +1136,13 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
        /* Add filter */
        if (server_supports_feature("fetch", "filter", 0) &&
            args->filter_options.choice) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
                print_verbose(args, _("Server supports filter"));
+               expand_list_objects_filter_spec(&args->filter_options,
+                                               &expanded_filter_spec);
                packet_buf_write(&req_buf, "filter %s",
-                                args->filter_options.filter_spec);
+                                expanded_filter_spec.buf);
+               strbuf_release(&expanded_filter_spec);
        } else if (args->filter_options.choice) {
                warning("filtering not recognized by server, ignoring");
        }
@@ -1267,6 +1252,8 @@ static int process_acks(struct fetch_negotiator *negotiator,
 static void receive_shallow_info(struct fetch_pack_args *args,
                                 struct packet_reader *reader)
 {
+       int line_received = 0;
+
        process_section_header(reader, "shallow-info", 0);
        while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
                const char *arg;
@@ -1276,6 +1263,7 @@ static void receive_shallow_info(struct fetch_pack_args *args,
                        if (get_oid_hex(arg, &oid))
                                die(_("invalid shallow line: %s"), reader->line);
                        register_shallow(the_repository, &oid);
+                       line_received = 1;
                        continue;
                }
                if (skip_prefix(reader->line, "unshallow ", &arg)) {
@@ -1288,6 +1276,7 @@ static void receive_shallow_info(struct fetch_pack_args *args,
                                die(_("error in object: %s"), reader->line);
                        if (unregister_shallow(&oid))
                                die(_("no shallow found: %s"), reader->line);
+                       line_received = 1;
                        continue;
                }
                die(_("expected shallow/unshallow, got %s"), reader->line);
@@ -1297,8 +1286,11 @@ static void receive_shallow_info(struct fetch_pack_args *args,
            reader->status != PACKET_READ_DELIM)
                die(_("error processing shallow info: %d"), reader->status);
 
-       setup_alternate_shallow(&shallow_lock, &alternate_shallow_file, NULL);
-       args->deepen = 1;
+       if (line_received) {
+               setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
+                                       NULL);
+               args->deepen = 1;
+       }
 }
 
 static void receive_wanted_refs(struct packet_reader *reader,
@@ -1351,7 +1343,13 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
        struct fetch_negotiator negotiator;
        fetch_negotiator_init(&negotiator, negotiation_algorithm);
        packet_reader_init(&reader, fd[0], NULL, 0,
-                          PACKET_READ_CHOMP_NEWLINE);
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
+       if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 1) &&
+           server_supports_feature("fetch", "sideband-all", 0)) {
+               reader.use_sideband = 1;
+               reader.me = "fetch-pack";
+       }
 
        while (state != FETCH_DONE) {
                switch (state) {
@@ -1385,7 +1383,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                case FETCH_SEND_REQUEST:
                        if (send_fetch_request(&negotiator, fd[1], args, ref,
                                               &common,
-                                              &haves_to_send, &in_vain))
+                                              &haves_to_send, &in_vain,
+                                              reader.use_sideband))
                                state = FETCH_GET_PACK;
                        else
                                state = FETCH_PROCESS_ACKS;
diff --git a/fsck.c b/fsck.c
index 68502ce85b11bf0ef4445f2b1688b3834301ba53..2260adb71e7a9f1091d14bb635a4f082592b9512 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -410,14 +410,14 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
                        continue;
 
                if (S_ISDIR(entry.mode)) {
-                       obj = (struct object *)lookup_tree(the_repository, entry.oid);
+                       obj = (struct object *)lookup_tree(the_repository, &entry.oid);
                        if (name && obj)
                                put_object_name(options, obj, "%s%s/", name,
                                        entry.path);
                        result = options->walk(obj, OBJ_TREE, data, options);
                }
                else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
-                       obj = (struct object *)lookup_blob(the_repository, entry.oid);
+                       obj = (struct object *)lookup_blob(the_repository, &entry.oid);
                        if (name && obj)
                                put_object_name(options, obj, "%s%s", name,
                                        entry.path);
diff --git a/fuzz-commit-graph.c b/fuzz-commit-graph.c
new file mode 100644 (file)
index 0000000..cf790c9
--- /dev/null
@@ -0,0 +1,16 @@
+#include "commit-graph.h"
+
+struct commit_graph *parse_commit_graph(void *graph_map, int fd,
+                                       size_t graph_size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       struct commit_graph *g;
+
+       g = parse_commit_graph((void *)data, -1, size);
+       free(g);
+
+       return 0;
+}
index 09b0102cae8c8c0e39dc239003ca599a896730cf..29a19902aaa00b90176e253c90c2384fca65857c 100644 (file)
 #endif
 
 #if defined(__CYGWIN__)
-#include "compat/cygwin.h"
+#include "compat/win32/path-utils.h"
 #endif
 #if defined(__MINGW32__)
 /* pull in Windows compatibility stuff */
+#include "compat/win32/path-utils.h"
 #include "compat/mingw.h"
 #elif defined(_MSC_VER)
 #include "compat/msvc.h"
@@ -397,6 +398,19 @@ static inline char *git_find_last_dir_sep(const char *path)
 #define query_user_email() NULL
 #endif
 
+#ifdef __TANDEM
+#include <floss.h(floss_execl,floss_execlp,floss_execv,floss_execvp)>
+#include <floss.h(floss_getpwuid)>
+#ifndef NSIG
+/*
+ * NonStop NSE and NSX do not provide NSIG. SIGGUARDIAN(99) is the highest
+ * known, by detective work using kill -l as a list is all signals
+ * instead of signal.h where it should be.
+ */
+# define NSIG 100
+#endif
+#endif
+
 #if defined(__HP_cc) && (__HP_cc >= 61000)
 #define NORETURN __attribute__((noreturn))
 #define NORETURN_PTR
@@ -721,7 +735,7 @@ extern const char *githstrerror(int herror);
 #ifdef NO_MEMMEM
 #define memmem gitmemmem
 void *gitmemmem(const void *haystack, size_t haystacklen,
-                const void *needle, size_t needlelen);
+               const void *needle, size_t needlelen);
 #endif
 
 #ifdef OVERRIDE_STRDUP
index eec264e6303684c5886804361c630e1572c1fe09..7c55229773e2bdb0f138b1180ab22d05c74ff904 100755 (executable)
@@ -67,6 +67,13 @@ resolve_full_httpd () {
                httpd_only="${httpd%% *}" # cut on first space
                return
                ;;
+       *python*)
+               # server is started by running via generated gitweb.py in
+               # $fqgitdir/gitweb
+               full_httpd="$fqgitdir/gitweb/gitweb.py"
+               httpd_only="${httpd%% *}" # cut on first space
+               return
+               ;;
        esac
 
        httpd_only="$(echo $httpd | cut -f1 -d' ')"
@@ -110,7 +117,7 @@ start_httpd () {
 
        # don't quote $full_httpd, there can be arguments to it (-f)
        case "$httpd" in
-       *mongoose*|*plackup*)
+       *mongoose*|*plackup*|*python*)
                #These servers don't have a daemon mode so we'll have to fork it
                $full_httpd "$conf" &
                #Save the pid before doing anything else (we'll print it later)
@@ -595,6 +602,121 @@ EOF
        rm -f "$conf"
 }
 
+python_conf() {
+       # Python's builtin http.server and its CGI support is very limited.
+       # CGI handler is capable of running CGI script only from inside a directory.
+       # Trying to set cgi_directories=["/"] will add double slash to SCRIPT_NAME
+       # and that in turn breaks gitweb's relative link generation.
+
+       # create a simple web root where $fqgitdir/gitweb/$httpd_only is our root
+       mkdir -p "$fqgitdir/gitweb/$httpd_only/cgi-bin"
+       # Python http.server follows the symlinks
+       ln -sf "$root/gitweb.cgi" "$fqgitdir/gitweb/$httpd_only/cgi-bin/gitweb.cgi"
+       ln -sf "$root/static" "$fqgitdir/gitweb/$httpd_only/"
+
+       # generate a standalone 'python http.server' script in $fqgitdir/gitweb
+       # This asumes that python is in user's $PATH
+       # This script is Python 2 and 3 compatible
+       cat > "$fqgitdir/gitweb/gitweb.py" <<EOF
+#!/usr/bin/env python
+import os
+import sys
+
+# Open log file in line buffering mode
+accesslogfile = open("$fqgitdir/gitweb/access.log", 'a', buffering=1)
+errorlogfile = open("$fqgitdir/gitweb/error.log", 'a', buffering=1)
+
+# and replace our stdout and stderr with log files
+# also do a lowlevel duplicate of the logfile file descriptors so that
+# our CGI child process writes any stderr warning also to the log file
+_orig_stdout_fd = sys.stdout.fileno()
+sys.stdout.close()
+os.dup2(accesslogfile.fileno(), _orig_stdout_fd)
+sys.stdout = accesslogfile
+
+_orig_stderr_fd = sys.stderr.fileno()
+sys.stderr.close()
+os.dup2(errorlogfile.fileno(), _orig_stderr_fd)
+sys.stderr = errorlogfile
+
+from functools import partial
+
+if sys.version_info < (3, 0):  # Python 2
+       from CGIHTTPServer import CGIHTTPRequestHandler
+       from BaseHTTPServer import HTTPServer as ServerClass
+else:  # Python 3
+       from http.server import CGIHTTPRequestHandler
+       from http.server import HTTPServer as ServerClass
+
+
+# Those environment variables will be passed to the cgi script
+os.environ.update({
+       "GIT_EXEC_PATH": "$GIT_EXEC_PATH",
+       "GIT_DIR": "$GIT_DIR",
+       "GITWEB_CONFIG": "$GITWEB_CONFIG"
+})
+
+
+class GitWebRequestHandler(CGIHTTPRequestHandler):
+
+       def log_message(self, format, *args):
+               # Write access logs to stdout
+               sys.stdout.write("%s - - [%s] %s\n" %
+                               (self.address_string(),
+                               self.log_date_time_string(),
+                               format%args))
+
+       def do_HEAD(self):
+               self.redirect_path()
+               CGIHTTPRequestHandler.do_HEAD(self)
+
+       def do_GET(self):
+               if self.path == "/":
+                       self.send_response(303, "See Other")
+                       self.send_header("Location", "/cgi-bin/gitweb.cgi")
+                       self.end_headers()
+                       return
+               self.redirect_path()
+               CGIHTTPRequestHandler.do_GET(self)
+
+       def do_POST(self):
+               self.redirect_path()
+               CGIHTTPRequestHandler.do_POST(self)
+
+       # rewrite path of every request that is not gitweb.cgi to out of cgi-bin
+       def redirect_path(self):
+               if not self.path.startswith("/cgi-bin/gitweb.cgi"):
+                       self.path = self.path.replace("/cgi-bin/", "/")
+
+       # gitweb.cgi is the only thing that is ever going to be run here.
+       # Ignore everything else
+       def is_cgi(self):
+               result = False
+               if self.path.startswith('/cgi-bin/gitweb.cgi'):
+                       result = CGIHTTPRequestHandler.is_cgi(self)
+               return result
+
+
+bind = "127.0.0.1"
+if "$local" == "true":
+       bind = "0.0.0.0"
+
+# Set our http root directory
+# This is a work around for a missing directory argument in older Python versions
+# as this was added to SimpleHTTPRequestHandler in Python 3.7
+os.chdir("$fqgitdir/gitweb/$httpd_only/")
+
+GitWebRequestHandler.protocol_version = "HTTP/1.0"
+httpd = ServerClass((bind, $port), GitWebRequestHandler)
+
+sa = httpd.socket.getsockname()
+print("Serving HTTP on", sa[0], "port", sa[1], "...")
+httpd.serve_forever()
+EOF
+
+       chmod a+x "$fqgitdir/gitweb/gitweb.py"
+}
+
 gitweb_conf() {
        cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
 #!@@PERL@@
@@ -623,6 +745,9 @@ configure_httpd() {
        *plackup*)
                plackup_conf
                ;;
+       *python*)
+               python_conf
+               ;;
        *)
                echo "Unknown httpd specified: $httpd"
                exit 1
index b4c7dbfa575d3b5c664677c22b8613284d2e33fb..8d6c9aca6584f76689246604abe047a85504671e 100755 (executable)
@@ -26,6 +26,7 @@ f,force-rebase!    cherry-pick all commits, even if unchanged
 m,merge!           use merging strategies to rebase
 i,interactive!     let the user edit the list of commits to rebase
 x,exec=!           add exec lines after each commit of the editable list
+y=!                same as --reschedule-failed-exec -x
 k,keep-empty      preserve empty commits during rebase
 allow-empty-message allow rebasing commits with empty messages
 stat!              display a diffstat of what changed upstream
@@ -48,6 +49,7 @@ skip!              skip current patch and continue
 edit-todo!         edit the todo list during an interactive rebase
 quit!              abort but keep HEAD where it is
 show-current-patch! show the patch file being applied or merged
+reschedule-failed-exec automatically reschedule failed exec commands
 "
 . git-sh-setup
 set_reflog_action rebase
@@ -92,11 +94,14 @@ autosquash=
 keep_empty=
 allow_empty_message=--allow-empty-message
 signoff=
+reschedule_failed_exec=
 test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
 case "$(git config --bool commit.gpgsign)" in
 true)  gpg_sign_opt=-S ;;
 *)     gpg_sign_opt= ;;
 esac
+test "$(git config --bool rebase.reschedulefailedexec)" = "true" &&
+reschedule_failed_exec=--reschedule-failed-exec
 . git-rebase--common
 
 read_basic_state () {
@@ -113,7 +118,7 @@ read_basic_state () {
        else
                orig_head=$(cat "$state_dir"/head)
        fi &&
-       GIT_QUIET=$(cat "$state_dir"/quiet) &&
+       test -f "$state_dir"/quiet && GIT_QUIET=t
        test -f "$state_dir"/verbose && verbose=t
        test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
        test -f "$state_dir"/strategy_opts &&
@@ -126,6 +131,8 @@ read_basic_state () {
                signoff="$(cat "$state_dir"/signoff)"
                force_rebase=t
        }
+       test -f "$state_dir"/reschedule-failed-exec &&
+               reschedule_failed_exec=t
 }
 
 finish_rebase () {
@@ -163,13 +170,14 @@ run_interactive () {
                "$allow_empty_message" "$autosquash" "$verbose" \
                "$force_rebase" "$onto_name" "$head_name" "$strategy" \
                "$strategy_opts" "$cmd" "$switch_to" \
-               "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff"
+               "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff" \
+               "$reschedule_failed_exec"
 }
 
 run_specific_rebase () {
        if [ "$interactive_rebase" = implied ]; then
-               GIT_EDITOR=:
-               export GIT_EDITOR
+               GIT_SEQUENCE_EDITOR=:
+               export GIT_SEQUENCE_EDITOR
                autosquash=
        fi
 
@@ -218,6 +226,7 @@ then
        state_dir="$apply_dir"
 elif test -d "$merge_dir"
 then
+       type=interactive
        if test -d "$merge_dir"/rewritten
        then
                type=preserve-merges
@@ -225,10 +234,7 @@ then
                preserve_merges=t
        elif test -f "$merge_dir"/interactive
        then
-               type=interactive
                interactive_rebase=explicit
-       else
-               type=merge
        fi
        state_dir="$merge_dir"
 fi
@@ -255,6 +261,11 @@ do
                cmd="${cmd}exec ${1#--exec=}${LF}"
                test -z "$interactive_rebase" && interactive_rebase=implied
                ;;
+       -y*)
+               reschedule_failed_exec=--reschedule-failed-exec
+               cmd="${cmd}exec ${1#-y}${LF}"
+               test -z "$interactive_rebase" && interactive_rebase=implied
+               ;;
        --interactive)
                interactive_rebase=explicit
                ;;
@@ -378,6 +389,12 @@ do
        --gpg-sign=*)
                gpg_sign_opt="-S${1#--gpg-sign=}"
                ;;
+       --reschedule-failed-exec)
+               reschedule_failed_exec=--reschedule-failed-exec
+               ;;
+       --no-reschedule-failed-exec)
+               reschedule_failed_exec=
+               ;;
        --)
                shift
                break
@@ -477,6 +494,7 @@ then
        test -z "$interactive_rebase" && interactive_rebase=implied
 fi
 
+actually_interactive=
 if test -n "$interactive_rebase"
 then
        if test -z "$preserve_merges"
@@ -485,11 +503,12 @@ then
        else
                type=preserve-merges
        fi
-
+       actually_interactive=t
        state_dir="$merge_dir"
 elif test -n "$do_merge"
 then
-       type=merge
+       interactive_rebase=implied
+       type=interactive
        state_dir="$merge_dir"
 else
        type=am
@@ -501,28 +520,20 @@ then
        git_format_patch_opt="$git_format_patch_opt --progress"
 fi
 
-if test -n "$git_am_opt"; then
-       incompatible_opts=$(echo " $git_am_opt " | \
-                           sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/')
-       if test -n "$interactive_rebase"
+incompatible_opts=$(echo " $git_am_opt " | \
+                   sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/')
+if test -n "$incompatible_opts"
+then
+       if test -n "$actually_interactive" || test "$do_merge"
        then
-               if test -n "$incompatible_opts"
-               then
-                       die "$(gettext "error: cannot combine interactive options (--interactive, --exec, --rebase-merges, --preserve-merges, --keep-empty, --root + --onto) with am options ($incompatible_opts)")"
-               fi
-       fi
-       if test -n "$do_merge"; then
-               if test -n "$incompatible_opts"
-               then
-                       die "$(gettext "error: cannot combine merge options (--merge, --strategy, --strategy-option) with am options ($incompatible_opts)")"
-               fi
+               die "$(gettext "fatal: cannot combine am options with either interactive or merge options")"
        fi
 fi
 
 if test -n "$signoff"
 then
        test -n "$preserve_merges" &&
-               die "$(gettext "error: cannot combine '--signoff' with '--preserve-merges'")"
+               die "$(gettext "fatal: cannot combine '--signoff' with '--preserve-merges'")"
        git_am_opt="$git_am_opt $signoff"
        force_rebase=t
 fi
@@ -533,15 +544,18 @@ then
        # Note: incompatibility with --interactive is just a strong warning;
        #       git-rebase.txt caveats with "unless you know what you are doing"
        test -n "$rebase_merges" &&
-               die "$(gettext "error: cannot combine '--preserve-merges' with '--rebase-merges'")"
+               die "$(gettext "fatal: cannot combine '--preserve-merges' with '--rebase-merges'")"
+
+       test -n "$reschedule_failed_exec" &&
+               die "$(gettext "error: cannot combine '--preserve-merges' with '--reschedule-failed-exec'")"
 fi
 
 if test -n "$rebase_merges"
 then
        test -n "$strategy_opts" &&
-               die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy-option'")"
+               die "$(gettext "fatal: cannot combine '--rebase-merges' with '--strategy-option'")"
        test -n "$strategy" &&
-               die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy'")"
+               die "$(gettext "fatal: cannot combine '--rebase-merges' with '--strategy'")"
 fi
 
 if test -z "$rebase_root"
@@ -680,7 +694,7 @@ require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
 # but this should be done only when upstream and onto are the same
 # and if this is not an interactive rebase.
 mb=$(git merge-base "$onto" "$orig_head")
-if test -z "$interactive_rebase" && test "$upstream" = "$onto" &&
+if test -z "$actually_interactive" && test "$upstream" = "$onto" &&
        test "$mb" = "$onto" && test -z "$restrict_revision" &&
        # linear history?
        ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
@@ -730,6 +744,19 @@ then
        GIT_PAGER='' git diff --stat --summary "$mb_tree" "$onto"
 fi
 
+if test -z "$actually_interactive" && test "$mb" = "$orig_head"
+then
+       say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")"
+       GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
+               git checkout -q "$onto^0" || die "could not detach HEAD"
+       # If the $onto is a proper descendant of the tip of the branch, then
+       # we just fast-forwarded.
+       git update-ref ORIG_HEAD $orig_head
+       move_to_original_branch
+       finish_rebase
+       exit 0
+fi
+
 test -n "$interactive_rebase" && run_specific_rebase
 
 # Detach HEAD and reset the tree
@@ -739,16 +766,6 @@ GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
        git checkout -q "$onto^0" || die "could not detach HEAD"
 git update-ref ORIG_HEAD $orig_head
 
-# If the $onto is a proper descendant of the tip of the branch, then
-# we just fast-forwarded.
-if test "$mb" = "$orig_head"
-then
-       say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")"
-       move_to_original_branch
-       finish_rebase
-       exit 0
-fi
-
 if test -n "$rebase_root"
 then
        revisions="$onto..$orig_head"
index 1998c3e141bec5142032f08623b916d7741e64ac..5b79920f46a972437ff05966d042a84d44f050d9 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -332,6 +332,8 @@ def p4_check_access(min_expiration=1):
             die_bad_access("p4 error: {0}".format(data))
         else:
             die_bad_access("unknown error")
+    elif code == "info":
+        return
     else:
         die_bad_access("unknown error code {0}".format(code))
 
@@ -1859,6 +1861,7 @@ class P4Submit(Command, P4UserMap):
                     filesToAdd.remove(path)
             elif modifier == "C":
                 src, dest = diff['src'], diff['dst']
+                all_files.append(dest)
                 p4_integrate(src, dest)
                 pureRenameCopy.add(dest)
                 if diff['src_sha1'] != diff['dst_sha1']:
@@ -1875,6 +1878,7 @@ class P4Submit(Command, P4UserMap):
                 editedFiles.add(dest)
             elif modifier == "R":
                 src, dest = diff['src'], diff['dst']
+                all_files.append(dest)
                 if self.p4HasMoveCommand:
                     p4_edit(src)        # src must be open before move
                     p4_move(src, dest)  # opens for (move/delete, move/add)
index 6d3a88decdeee3f85d9ee43ef8e716ccd1a6328b..e3d390974331e83261ba32e757724723b9bea724 100755 (executable)
@@ -8,6 +8,7 @@ n,dry-run     dry run
 author=       author name and email address for patches without any
 patches=      path to the quilt patches
 series=       path to the quilt series file
+keep-non-patch Pass -b to git mailinfo
 "
 SUBDIRECTORY_ON=Yes
 . git-sh-setup
@@ -32,6 +33,9 @@ do
                shift
                QUILT_SERIES="$1"
                ;;
+       --keep-non-patch)
+               MAILINFO_OPT="-b"
+               ;;
        --)
                shift
                break;;
@@ -98,7 +102,7 @@ do
                continue
        fi
        echo $patch_name
-       git mailinfo "$tmp_msg" "$tmp_patch" \
+       git mailinfo $MAILINFO_OPT "$tmp_msg" "$tmp_patch" \
                <"$QUILT_PATCHES/$patch_name" >"$tmp_info" || exit 3
        test -s "$tmp_patch" || {
                echo "Patch is empty.  Was it split wrong?"
index 99b8c177875a7f26ae6c7f70be42c9d97231f7b9..6416716ee620cfac7803f9f00cef309a41b08f31 100644 (file)
@@ -36,7 +36,7 @@ rm -f "$GIT_DIR/rebased-patches"
 
 git format-patch -k --stdout --full-index --cherry-pick --right-only \
        --src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
-       --pretty=mboxrd \
+       --pretty=mboxrd --topo-order \
        $git_format_patch_opt \
        "$revisions" ${restrict_revision+^$restrict_revision} \
        >"$GIT_DIR/rebased-patches"
index 7e39d228717899228dfd735b22196de973e7add3..f00e13e5d04df09558dba65afd4fe1ed44375fa9 100644 (file)
@@ -10,7 +10,7 @@ write_basic_state () {
        echo "$head_name" > "$state_dir"/head-name &&
        echo "$onto" > "$state_dir"/onto &&
        echo "$orig_head" > "$state_dir"/orig-head &&
-       echo "$GIT_QUIET" > "$state_dir"/quiet &&
+       test t = "$GIT_QUIET" && : > "$state_dir"/quiet
        test t = "$verbose" && : > "$state_dir"/verbose
        test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
        test -n "$strategy_opts" && echo "$strategy_opts" > \
@@ -19,6 +19,7 @@ write_basic_state () {
                "$state_dir"/allow_rerere_autoupdate
        test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > "$state_dir"/gpg_sign_opt
        test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff
+       test -n "$reschedule_failed_exec" && : > "$state_dir"/reschedule-failed-exec
 }
 
 apply_autostash () {
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
deleted file mode 100644 (file)
index aa2f2f0..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-# This shell script fragment is sourced by git-rebase to implement
-# its merge-based non-interactive mode that copes well with renamed
-# files.
-#
-# Copyright (c) 2010 Junio C Hamano.
-#
-
-prec=4
-
-read_state () {
-       onto_name=$(cat "$state_dir"/onto_name) &&
-       end=$(cat "$state_dir"/end) &&
-       msgnum=$(cat "$state_dir"/msgnum)
-}
-
-continue_merge () {
-       test -d "$state_dir" || die "$state_dir directory does not exist"
-
-       unmerged=$(git ls-files -u)
-       if test -n "$unmerged"
-       then
-               echo "You still have unmerged paths in your index"
-               echo "did you forget to use git add?"
-               die "$resolvemsg"
-       fi
-
-       cmt=$(cat "$state_dir/current")
-       if ! git diff-index --quiet --ignore-submodules HEAD --
-       then
-               if ! git commit ${gpg_sign_opt:+"$gpg_sign_opt"} $signoff $allow_empty_message \
-                       --no-verify -C "$cmt"
-               then
-                       echo "Commit failed, please do not call \"git commit\""
-                       echo "directly, but instead do one of the following: "
-                       die "$resolvemsg"
-               fi
-               if test -z "$GIT_QUIET"
-               then
-                       printf "Committed: %0${prec}d " $msgnum
-               fi
-               echo "$cmt $(git rev-parse HEAD^0)" >> "$state_dir/rewritten"
-       else
-               if test -z "$GIT_QUIET"
-               then
-                       printf "Already applied: %0${prec}d " $msgnum
-               fi
-       fi
-       test -z "$GIT_QUIET" &&
-       GIT_PAGER='' git log --format=%s -1 "$cmt"
-
-       # onto the next patch:
-       msgnum=$(($msgnum + 1))
-       echo "$msgnum" >"$state_dir/msgnum"
-}
-
-call_merge () {
-       msgnum="$1"
-       echo "$msgnum" >"$state_dir/msgnum"
-       cmt="$(cat "$state_dir/cmt.$msgnum")"
-       echo "$cmt" > "$state_dir/current"
-       git update-ref REBASE_HEAD "$cmt"
-       hd=$(git rev-parse --verify HEAD)
-       cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
-       eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
-       eval GITHEAD_$hd='$onto_name'
-       export GITHEAD_$cmt GITHEAD_$hd
-       if test -n "$GIT_QUIET"
-       then
-               GIT_MERGE_VERBOSITY=1 && export GIT_MERGE_VERBOSITY
-       fi
-       test -z "$strategy" && strategy=recursive
-       # 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)
-               unset GITHEAD_$cmt GITHEAD_$hd
-               return
-               ;;
-       1)
-               git rerere $allow_rerere_autoupdate
-               die "$resolvemsg"
-               ;;
-       2)
-               echo "Strategy: $strategy failed, try another" 1>&2
-               die "$resolvemsg"
-               ;;
-       *)
-               die "Unknown exit code ($rv) from command:" \
-                       "git merge-$strategy $cmt^ -- HEAD $cmt"
-               ;;
-       esac
-}
-
-finish_rb_merge () {
-       move_to_original_branch
-       if test -s "$state_dir"/rewritten
-       then
-               git notes copy --for-rewrite=rebase <"$state_dir"/rewritten
-               hook="$(git rev-parse --git-path hooks/post-rewrite)"
-               test -x "$hook" && "$hook" rebase <"$state_dir"/rewritten
-       fi
-       say All done.
-}
-
-git_rebase__merge () {
-
-case "$action" in
-continue)
-       read_state
-       continue_merge
-       while test "$msgnum" -le "$end"
-       do
-               call_merge "$msgnum"
-               continue_merge
-       done
-       finish_rb_merge
-       return
-       ;;
-skip)
-       read_state
-       git rerere clear
-       msgnum=$(($msgnum + 1))
-       while test "$msgnum" -le "$end"
-       do
-               call_merge "$msgnum"
-               continue_merge
-       done
-       finish_rb_merge
-       return
-       ;;
-show-current-patch)
-       exec git show REBASE_HEAD --
-       ;;
-esac
-
-mkdir -p "$state_dir"
-echo "$onto_name" > "$state_dir/onto_name"
-write_basic_state
-rm -f "$(git rev-parse --git-path REBASE_HEAD)"
-
-msgnum=0
-for cmt in $(git rev-list --reverse --no-merges "$revisions")
-do
-       msgnum=$(($msgnum + 1))
-       echo "$cmt" > "$state_dir/cmt.$msgnum"
-done
-
-echo 1 >"$state_dir/msgnum"
-echo $msgnum >"$state_dir/end"
-
-end=$msgnum
-msgnum=1
-
-while test "$msgnum" -le "$end"
-do
-       call_merge "$msgnum"
-       continue_merge
-done
-
-finish_rb_merge
-
-}
index 94793c1a913abf569ff9101d935c355b9eb27648..789ce2f41d4a3c6e88084412bcc2b7be6ae98526 100755 (executable)
@@ -55,6 +55,20 @@ untracked_files () {
        git ls-files -o $z $excl_opt -- "$@"
 }
 
+prepare_fallback_ident () {
+       if ! git -c user.useconfigonly=yes var GIT_COMMITTER_IDENT >/dev/null 2>&1
+       then
+               GIT_AUTHOR_NAME="git stash"
+               GIT_AUTHOR_EMAIL=git@stash
+               GIT_COMMITTER_NAME="git stash"
+               GIT_COMMITTER_EMAIL=git@stash
+               export GIT_AUTHOR_NAME
+               export GIT_AUTHOR_EMAIL
+               export GIT_COMMITTER_NAME
+               export GIT_COMMITTER_EMAIL
+       fi
+}
+
 clear_stash () {
        if test $# != 0
        then
@@ -67,6 +81,9 @@ clear_stash () {
 }
 
 create_stash () {
+
+       prepare_fallback_ident
+
        stash_msg=
        untracked=
        while test $# != 0
diff --git a/git.c b/git.c
index 2f604a41eaf620f4d185c612cd7c6aee975faa67..0ce0e13f0f4879ae4608bd61d3a6a1f55ef7892e 100644 (file)
--- a/git.c
+++ b/git.c
@@ -98,7 +98,8 @@ static int list_cmds(const char *spec)
        return 0;
 }
 
-static void commit_pager_choice(void) {
+static void commit_pager_choice(void)
+{
        switch (use_pager) {
        case 0:
                setenv("GIT_PAGER", "cat", 1);
@@ -338,27 +339,27 @@ static int handle_alias(int *argcp, const char ***argv)
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
 
-                       die_errno("while expanding alias '%s': '%s'",
-                           alias_command, alias_string + 1);
+                       die_errno(_("while expanding alias '%s': '%s'"),
+                                 alias_command, alias_string + 1);
                }
                count = split_cmdline(alias_string, &new_argv);
                if (count < 0)
-                       die("Bad alias.%s string: %s", alias_command,
-                           split_cmdline_strerror(count));
+                       die(_("bad alias.%s string: %s"), alias_command,
+                           _(split_cmdline_strerror(count)));
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
-                       die("alias '%s' changes environment variables.\n"
-                                "You can use '!git' in the alias to do this",
-                                alias_command);
+                       die(_("alias '%s' changes environment variables.\n"
+                             "You can use '!git' in the alias to do this"),
+                           alias_command);
                memmove(new_argv - option_count, new_argv,
                                count * sizeof(char *));
                new_argv -= option_count;
 
                if (count < 1)
-                       die("empty alias for %s", alias_command);
+                       die(_("empty alias for %s"), alias_command);
 
                if (!strcmp(alias_command, new_argv[0]))
-                       die("recursive alias: %s", alias_command);
+                       die(_("recursive alias: %s"), alias_command);
 
                trace_argv_printf(new_argv,
                                  "trace: alias expansion: %s =>",
@@ -409,7 +410,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 
        if (!help && get_super_prefix()) {
                if (!(p->option & SUPPORT_SUPER_PREFIX))
-                       die("%s doesn't support --super-prefix", p->cmd);
+                       die(_("%s doesn't support --super-prefix"), p->cmd);
        }
 
        if (!help && p->option & NEED_WORK_TREE)
@@ -433,11 +434,11 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 
        /* Check for ENOSPC and EIO errors.. */
        if (fflush(stdout))
-               die_errno("write failure on standard output");
+               die_errno(_("write failure on standard output"));
        if (ferror(stdout))
-               die("unknown write failure on standard output");
+               die(_("unknown write failure on standard output"));
        if (fclose(stdout))
-               die_errno("close failed on standard output");
+               die_errno(_("close failed on standard output"));
        return 0;
 }
 
@@ -654,7 +655,7 @@ static void execv_dashed_external(const char **argv)
        int status;
 
        if (get_super_prefix())
-               die("%s doesn't support --super-prefix", argv[0]);
+               die(_("%s doesn't support --super-prefix"), argv[0]);
 
        if (use_pager == -1 && !is_builtin(argv[0]))
                use_pager = check_pager_config(argv[0]);
@@ -766,7 +767,7 @@ int cmd_main(int argc, const char **argv)
        if (skip_prefix(cmd, "git-", &cmd)) {
                argv[0] = cmd;
                handle_builtin(argc, argv);
-               die("cannot handle %s as a builtin", cmd);
+               die(_("cannot handle %s as a builtin"), cmd);
        }
 
        /* Look for flags.. */
@@ -779,7 +780,7 @@ int cmd_main(int argc, const char **argv)
        } else {
                /* The user didn't specify a command; give them help */
                commit_pager_choice();
-               printf("usage: %s\n\n", git_usage_string);
+               printf(_("usage: %s\n\n"), git_usage_string);
                list_common_cmds_help();
                printf("\n%s\n", _(git_more_info_string));
                exit(1);
diff --git a/grep.c b/grep.c
index 4db1510d167c9fc3cc7b129c90aa874a4c7c2b0f..0d50598acda6f1c41951d1d656ea701114473239 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1805,7 +1805,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                 * is not thread-safe.
                 */
                grep_attr_lock();
-               textconv = userdiff_get_textconv(gs->driver);
+               textconv = userdiff_get_textconv(opt->repo, gs->driver);
                grep_attr_unlock();
        }
 
diff --git a/hash.h b/hash.h
index 7c8238bc2ebfded778351b58c16a3854617082c6..adde708cf26d90ffd7ae10e6cdb8bfd7c08b1db5 100644 (file)
--- a/hash.h
+++ b/hash.h
 #include "block-sha1/sha1.h"
 #endif
 
+#if defined(SHA256_GCRYPT)
+#include "sha256/gcrypt.h"
+#elif defined(SHA256_OPENSSL)
+#include <openssl/sha.h>
+#else
+#include "sha256/block/sha256.h"
+#endif
+
 #ifndef platform_SHA_CTX
 /*
  * platform's underlying implementation of SHA-1; could be OpenSSL,
 #define git_SHA1_Update                platform_SHA1_Update
 #define git_SHA1_Final         platform_SHA1_Final
 
+#ifndef platform_SHA256_CTX
+#define platform_SHA256_CTX    SHA256_CTX
+#define platform_SHA256_Init   SHA256_Init
+#define platform_SHA256_Update SHA256_Update
+#define platform_SHA256_Final  SHA256_Final
+#endif
+
+#define git_SHA256_CTX         platform_SHA256_CTX
+#define git_SHA256_Init                platform_SHA256_Init
+#define git_SHA256_Update      platform_SHA256_Update
+#define git_SHA256_Final       platform_SHA256_Final
+
 #ifdef SHA1_MAX_BLOCK_SIZE
 #include "compat/sha1-chunked.h"
 #undef git_SHA1_Update
 #define GIT_HASH_UNKNOWN 0
 /* SHA-1 */
 #define GIT_HASH_SHA1 1
+/* SHA-256  */
+#define GIT_HASH_SHA256 2
 /* Number of algorithms supported (including unknown). */
-#define GIT_HASH_NALGOS (GIT_HASH_SHA1 + 1)
+#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
 
 /* A suitably aligned type for stack allocations of hash contexts. */
 union git_hash_ctx {
        git_SHA_CTX sha1;
+       git_SHA256_CTX sha256;
 };
 typedef union git_hash_ctx git_hash_ctx;
 
@@ -81,6 +104,9 @@ struct git_hash_algo {
        /* The length of the hash in hex characters. */
        size_t hexsz;
 
+       /* The block size of the hash. */
+       size_t blksz;
+
        /* The hash initialization function. */
        git_hash_init_fn init_fn;
 
@@ -98,4 +124,17 @@ struct git_hash_algo {
 };
 extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
 
+/*
+ * Return a GIT_HASH_* constant based on the name.  Returns GIT_HASH_UNKNOWN if
+ * the name doesn't match a known algorithm.
+ */
+int hash_algo_by_name(const char *name);
+/* Identical, except based on the format ID. */
+int hash_algo_by_id(uint32_t format_id);
+/* Identical, except for a pointer to struct git_hash_algo. */
+static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
+{
+       return p - hash_algos;
+}
+
 #endif
diff --git a/hex.c b/hex.c
index 10af1a29e80f903fb95cbeb71be00e5fdd705e66..7850a8879d5e0a00c0e89e197427cafa23d8e865 100644 (file)
--- a/hex.c
+++ b/hex.c
@@ -73,14 +73,15 @@ int parse_oid_hex(const char *hex, struct object_id *oid, const char **end)
        return ret;
 }
 
-char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
+char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash,
+                         const struct git_hash_algo *algop)
 {
        static const char hex[] = "0123456789abcdef";
        char *buf = buffer;
        int i;
 
-       for (i = 0; i < the_hash_algo->rawsz; i++) {
-               unsigned int val = *sha1++;
+       for (i = 0; i < algop->rawsz; i++) {
+               unsigned int val = *hash++;
                *buf++ = hex[val >> 4];
                *buf++ = hex[val & 0xf];
        }
@@ -89,20 +90,35 @@ char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
        return buffer;
 }
 
+char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
+{
+       return hash_to_hex_algop_r(buffer, sha1, &hash_algos[GIT_HASH_SHA1]);
+}
+
 char *oid_to_hex_r(char *buffer, const struct object_id *oid)
 {
-       return sha1_to_hex_r(buffer, oid->hash);
+       return hash_to_hex_algop_r(buffer, oid->hash, the_hash_algo);
 }
 
-char *sha1_to_hex(const unsigned char *sha1)
+char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *algop)
 {
        static int bufno;
        static char hexbuffer[4][GIT_MAX_HEXSZ + 1];
        bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
-       return sha1_to_hex_r(hexbuffer[bufno], sha1);
+       return hash_to_hex_algop_r(hexbuffer[bufno], hash, algop);
+}
+
+char *sha1_to_hex(const unsigned char *sha1)
+{
+       return hash_to_hex_algop(sha1, &hash_algos[GIT_HASH_SHA1]);
+}
+
+char *hash_to_hex(const unsigned char *hash)
+{
+       return hash_to_hex_algop(hash, the_hash_algo);
 }
 
 char *oid_to_hex(const struct object_id *oid)
 {
-       return sha1_to_hex(oid->hash);
+       return hash_to_hex_algop(oid->hash, the_hash_algo);
 }
index 9e894f197f91ee3565b0f3c618fdb4042e2f229f..29e68e38b5d5466126608642a96b9640aca44272 100644 (file)
@@ -486,6 +486,8 @@ static void run_service(const char **argv, int buffer_input)
        if (buffer_input || gzipped_request || req_len >= 0)
                cld.in = -1;
        cld.git_cmd = 1;
+       cld.clean_on_exit = 1;
+       cld.wait_after_clean = 1;
        if (start_command(&cld))
                exit(1);
 
index cd485909127a79afcbb58ef18cd28977b65efb79..bb802d80ee08945b0008d8f6862e29c783410b35 100644 (file)
@@ -1311,11 +1311,11 @@ 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(the_repository, entry.oid),
+                       p = process_tree(lookup_tree(the_repository, &entry.oid),
                                         p);
                        break;
                case OBJ_BLOB:
-                       p = process_blob(lookup_blob(the_repository, entry.oid),
+                       p = process_blob(lookup_blob(the_repository, &entry.oid),
                                         p);
                        break;
                default:
index b3334bf657b5ea64e779eb470525eb0b7337baa9..0a392c85b67af254ddeca382faa79458c4ec3ca8 100644 (file)
@@ -547,7 +547,7 @@ static int fetch_object(struct walker *walker, unsigned char *sha1)
                ret = error("File %s has bad hash", hex);
        } else if (req->rename < 0) {
                struct strbuf buf = STRBUF_INIT;
-               sha1_file_name(the_repository, &buf, req->sha1);
+               loose_object_path(the_repository, &buf, req->sha1);
                ret = error("unable to write sha1 filename %s", buf.buf);
                strbuf_release(&buf);
        }
diff --git a/http.c b/http.c
index eacc2a75ef2e41da9d5f5741defcf1952e6c01a3..954bebf6842510ea3fb83334ffe2684ade072f05 100644 (file)
--- a/http.c
+++ b/http.c
@@ -48,6 +48,7 @@ char curl_errorstr[CURL_ERROR_SIZE];
 
 static int curl_ssl_verify = -1;
 static int curl_ssl_try;
+static const char *curl_http_version = NULL;
 static const char *ssl_cert;
 static const char *ssl_cipherlist;
 static const char *ssl_version;
@@ -284,6 +285,9 @@ static void process_curl_messages(void)
 
 static int http_options(const char *var, const char *value, void *cb)
 {
+       if (!strcmp("http.version", var)) {
+               return git_config_string(&curl_http_version, var, value);
+       }
        if (!strcmp("http.sslverify", var)) {
                curl_ssl_verify = git_config_bool(var, value);
                return 0;
@@ -789,6 +793,31 @@ static long get_curl_allowed_protocols(int from_user)
 }
 #endif
 
+#if LIBCURL_VERSION_NUM >=0x072f00
+static int get_curl_http_version_opt(const char *version_string, long *opt)
+{
+       int i;
+       static struct {
+               const char *name;
+               long opt_token;
+       } choice[] = {
+               { "HTTP/1.1", CURL_HTTP_VERSION_1_1 },
+               { "HTTP/2", CURL_HTTP_VERSION_2 }
+       };
+
+       for (i = 0; i < ARRAY_SIZE(choice); i++) {
+               if (!strcmp(version_string, choice[i].name)) {
+                       *opt = choice[i].opt_token;
+                       return 0;
+               }
+       }
+
+       warning("unknown value given to http.version: '%s'", version_string);
+       return -1; /* not found */
+}
+
+#endif
+
 static CURL *get_curl_handle(void)
 {
        CURL *result = curl_easy_init();
@@ -806,6 +835,16 @@ static CURL *get_curl_handle(void)
                curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
        }
 
+#if LIBCURL_VERSION_NUM >= 0x072f00 // 7.47.0
+    if (curl_http_version) {
+               long opt;
+               if (!get_curl_http_version_opt(curl_http_version, &opt)) {
+                       /* Set request use http version */
+                       curl_easy_setopt(result, CURLOPT_HTTP_VERSION, opt);
+               }
+    }
+#endif
+
 #if LIBCURL_VERSION_NUM >= 0x070907
        curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
 #endif
@@ -1837,8 +1876,6 @@ static int http_request(const char *url,
        strbuf_addstr(&buf, "Pragma:");
        if (options && options->no_cache)
                strbuf_addstr(&buf, " no-cache");
-       if (options && options->keep_error)
-               curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
        if (options && options->initial_request &&
            http_follow_config == HTTP_FOLLOW_INITIAL)
                curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);
@@ -1856,6 +1893,7 @@ static int http_request(const char *url,
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
+       curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
 
        ret = run_one_slot(slot, &results);
 
@@ -1950,19 +1988,26 @@ static int http_request_reauth(const char *url,
                return ret;
 
        /*
-        * If we are using KEEP_ERROR, the previous request may have
-        * put cruft into our output stream; we should clear it out before
-        * making our next request. We only know how to do this for
-        * the strbuf case, but that is enough to satisfy current callers.
+        * The previous request may have put cruft into our output stream; we
+        * should clear it out before making our next request.
         */
-       if (options && options->keep_error) {
-               switch (target) {
-               case HTTP_REQUEST_STRBUF:
-                       strbuf_reset(result);
-                       break;
-               default:
-                       BUG("HTTP_KEEP_ERROR is only supported with strbufs");
+       switch (target) {
+       case HTTP_REQUEST_STRBUF:
+               strbuf_reset(result);
+               break;
+       case HTTP_REQUEST_FILE:
+               if (fflush(result)) {
+                       error_errno("unable to flush a file");
+                       return HTTP_START_FAILED;
+               }
+               rewind(result);
+               if (ftruncate(fileno(result), 0) < 0) {
+                       error_errno("unable to truncate a file");
+                       return HTTP_START_FAILED;
                }
+               break;
+       default:
+               BUG("Unknown http_request target");
        }
 
        credential_fill(&http_auth);
@@ -2314,7 +2359,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
        hashcpy(freq->sha1, sha1);
        freq->localfile = -1;
 
-       sha1_file_name(the_repository, &filename, sha1);
+       loose_object_path(the_repository, &filename, sha1);
        strbuf_addf(&freq->tmpfile, "%s.temp", filename.buf);
 
        strbuf_addf(&prevfile, "%s.prev", filename.buf);
@@ -2465,7 +2510,7 @@ int finish_http_object_request(struct http_object_request *freq)
                unlink_or_warn(freq->tmpfile.buf);
                return -1;
        }
-       sha1_file_name(the_repository, &filename, freq->sha1);
+       loose_object_path(the_repository, &filename, freq->sha1);
        freq->rename = finalize_object_file(freq->tmpfile.buf, filename.buf);
        strbuf_release(&filename);
 
diff --git a/http.h b/http.h
index d305ca1dc7a3f931a81353c56060e98f4039c692..eebf40688cf6f695d12f9fe78e8e6b99bb44a8c4 100644 (file)
--- a/http.h
+++ b/http.h
@@ -146,7 +146,6 @@ extern char *get_remote_object_url(const char *url, const char *hex,
 /* Options for http_get_*() */
 struct http_get_options {
        unsigned no_cache:1,
-                keep_error:1,
                 initial_request:1;
 
        /* If non-NULL, returns the content-type of the response. */
index b4eb886e2a6a40bce1e478d8d24f43f64ed2e3aa..18ca6ba10a81931e6b789fa2d25d6dc2e1767eec 100644 (file)
@@ -1471,7 +1471,8 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
 }
 
 static int curl_append_msgs_to_imap(struct imap_server_conf *server,
-                                   struct strbuf* all_msgs, int total) {
+                                   struct strbuf* all_msgs, int total)
+{
        int ofs = 0;
        int n = 0;
        struct buffer msgbuf = { STRBUF_INIT, 0 };
index d1d429d73818cf4ef47b3e9e64c1e39489200be5..24e21731c4315f414578b33178a7b784176bb24d 100644 (file)
@@ -479,7 +479,7 @@ static struct commit *check_single_commit(struct rev_info *revs)
                struct object *obj = revs->pending.objects[i].item;
                if (obj->flags & UNINTERESTING)
                        continue;
-               obj = deref_tag(the_repository, obj, NULL, 0);
+               obj = deref_tag(revs->repo, obj, NULL, 0);
                if (obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (commit)
index e8da2e8581b2df956411f19e796ceb94adcef903..9efb3e99023877c804cf0dcf6cf100d6c09c716b 100644 (file)
@@ -18,8 +18,9 @@
  * See Documentation/rev-list-options.txt for allowed values for <arg>.
  *
  * Capture the given arg as the "filter_spec".  This can be forwarded to
- * subordinate commands when necessary.  We also "intern" the arg for
- * the convenience of the current command.
+ * subordinate commands when necessary (although it's better to pass it through
+ * expand_list_objects_filter_spec() first).  We also "intern" the arg for the
+ * convenience of the current command.
  */
 static int gently_parse_list_objects_filter(
        struct list_objects_filter_options *filter_options,
@@ -50,16 +51,15 @@ static int gently_parse_list_objects_filter(
                }
 
        } else if (skip_prefix(arg, "tree:", &v0)) {
-               unsigned long depth;
-               if (!git_parse_ulong(v0, &depth) || depth != 0) {
+               if (!git_parse_ulong(v0, &filter_options->tree_exclude_depth)) {
                        if (errbuf) {
                                strbuf_addstr(
                                        errbuf,
-                                       _("only 'tree:0' is supported"));
+                                       _("expected 'tree:<depth>'"));
                        }
                        return 1;
                }
-               filter_options->choice = LOFC_TREE_NONE;
+               filter_options->choice = LOFC_TREE_DEPTH;
                return 0;
 
        } else if (skip_prefix(arg, "sparse:oid=", &v0)) {
@@ -112,6 +112,21 @@ int opt_parse_list_objects_filter(const struct option *opt,
        return parse_list_objects_filter(filter_options, arg);
 }
 
+void expand_list_objects_filter_spec(
+       const struct list_objects_filter_options *filter,
+       struct strbuf *expanded_spec)
+{
+       strbuf_init(expanded_spec, strlen(filter->filter_spec));
+       if (filter->choice == LOFC_BLOB_LIMIT)
+               strbuf_addf(expanded_spec, "blob:limit=%lu",
+                           filter->blob_limit_value);
+       else if (filter->choice == LOFC_TREE_DEPTH)
+               strbuf_addf(expanded_spec, "tree:%lu",
+                           filter->tree_exclude_depth);
+       else
+               strbuf_addstr(expanded_spec, filter->filter_spec);
+}
+
 void list_objects_filter_release(
        struct list_objects_filter_options *filter_options)
 {
index af64e5c66fc4a242f9cc534380a9b3e8f1e67d00..e3adc78ebf74717d3b4e544c5bbb5215d9402a92 100644 (file)
@@ -2,6 +2,7 @@
 #define LIST_OBJECTS_FILTER_OPTIONS_H
 
 #include "parse-options.h"
+#include "strbuf.h"
 
 /*
  * The list of defined filters for list-objects.
@@ -10,7 +11,7 @@ enum list_objects_filter_choice {
        LOFC_DISABLED = 0,
        LOFC_BLOB_NONE,
        LOFC_BLOB_LIMIT,
-       LOFC_TREE_NONE,
+       LOFC_TREE_DEPTH,
        LOFC_SPARSE_OID,
        LOFC_SPARSE_PATH,
        LOFC__COUNT /* must be last */
@@ -20,8 +21,9 @@ struct list_objects_filter_options {
        /*
         * 'filter_spec' is the raw argument value given on the command line
         * or protocol request.  (The part after the "--keyword=".)  For
-        * commands that launch filtering sub-processes, this value should be
-        * passed to them as received by the current process.
+        * commands that launch filtering sub-processes, or for communication
+        * over the network, don't use this value; use the result of
+        * expand_list_objects_filter_spec() instead.
         */
        char *filter_spec;
 
@@ -44,6 +46,7 @@ struct list_objects_filter_options {
        struct object_id *sparse_oid_value;
        char *sparse_path_value;
        unsigned long blob_limit_value;
+       unsigned long tree_exclude_depth;
 };
 
 /* Normalized command line arguments */
@@ -61,6 +64,17 @@ int opt_parse_list_objects_filter(const struct option *opt,
          N_("object filtering"), 0, \
          opt_parse_list_objects_filter }
 
+/*
+ * Translates abbreviated numbers in the filter's filter_spec into their
+ * fully-expanded forms (e.g., "limit:blob=1k" becomes "limit:blob=1024").
+ *
+ * This form should be used instead of the raw filter_spec field when
+ * communicating with a remote process or subprocess.
+ */
+void expand_list_objects_filter_spec(
+       const struct list_objects_filter_options *filter,
+       struct strbuf *expanded_spec);
+
 void list_objects_filter_release(
        struct list_objects_filter_options *filter_options);
 
index 765f3df3b067c5df7b98402ef4fc953edcd9179b..ee449de3f77e2b8663d1ae2d43da049f15269249 100644 (file)
@@ -10,6 +10,7 @@
 #include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
+#include "oidmap.h"
 #include "oidset.h"
 #include "object-store.h"
 
@@ -34,6 +35,7 @@ struct filter_blobs_none_data {
 };
 
 static enum list_objects_filter_result filter_blobs_none(
+       struct repository *r,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
        const char *pathname,
@@ -83,54 +85,136 @@ static void *filter_blobs_none__init(
  * A filter for list-objects to omit ALL trees and blobs from the traversal.
  * Can OPTIONALLY collect a list of the omitted OIDs.
  */
-struct filter_trees_none_data {
+struct filter_trees_depth_data {
        struct oidset *omits;
+
+       /*
+        * Maps trees to the minimum depth at which they were seen. It is not
+        * necessary to re-traverse a tree at deeper or equal depths than it has
+        * already been traversed.
+        *
+        * We can't use LOFR_MARK_SEEN for tree objects since this will prevent
+        * it from being traversed at shallower depths.
+        */
+       struct oidmap seen_at_depth;
+
+       unsigned long exclude_depth;
+       unsigned long current_depth;
 };
 
-static enum list_objects_filter_result filter_trees_none(
+struct seen_map_entry {
+       struct oidmap_entry base;
+       size_t depth;
+};
+
+/* Returns 1 if the oid was in the omits set before it was invoked. */
+static int filter_trees_update_omits(
+       struct object *obj,
+       struct filter_trees_depth_data *filter_data,
+       int include_it)
+{
+       if (!filter_data->omits)
+               return 0;
+
+       if (include_it)
+               return oidset_remove(filter_data->omits, &obj->oid);
+       else
+               return oidset_insert(filter_data->omits, &obj->oid);
+}
+
+static enum list_objects_filter_result filter_trees_depth(
+       struct repository *r,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
        const char *pathname,
        const char *filename,
        void *filter_data_)
 {
-       struct filter_trees_none_data *filter_data = filter_data_;
+       struct filter_trees_depth_data *filter_data = filter_data_;
+       struct seen_map_entry *seen_info;
+       int include_it = filter_data->current_depth <
+               filter_data->exclude_depth;
+       int filter_res;
+       int already_seen;
+
+       /*
+        * Note that we do not use _MARK_SEEN in order to allow re-traversal in
+        * case we encounter a tree or blob again at a shallower depth.
+        */
 
        switch (filter_situation) {
        default:
                BUG("unknown filter_situation: %d", filter_situation);
 
-       case LOFS_BEGIN_TREE:
+       case LOFS_END_TREE:
+               assert(obj->type == OBJ_TREE);
+               filter_data->current_depth--;
+               return LOFR_ZERO;
+
        case LOFS_BLOB:
-               if (filter_data->omits) {
-                       oidset_insert(filter_data->omits, &obj->oid);
-                       /* _MARK_SEEN but not _DO_SHOW (hard omit) */
-                       return LOFR_MARK_SEEN;
+               filter_trees_update_omits(obj, filter_data, include_it);
+               return include_it ? LOFR_MARK_SEEN | LOFR_DO_SHOW : LOFR_ZERO;
+
+       case LOFS_BEGIN_TREE:
+               seen_info = oidmap_get(
+                       &filter_data->seen_at_depth, &obj->oid);
+               if (!seen_info) {
+                       seen_info = xcalloc(1, sizeof(*seen_info));
+                       oidcpy(&seen_info->base.oid, &obj->oid);
+                       seen_info->depth = filter_data->current_depth;
+                       oidmap_put(&filter_data->seen_at_depth, seen_info);
+                       already_seen = 0;
                } else {
-                       /*
-                        * Not collecting omits so no need to to traverse tree.
-                        */
-                       return LOFR_SKIP_TREE | LOFR_MARK_SEEN;
+                       already_seen =
+                               filter_data->current_depth >= seen_info->depth;
                }
 
-       case LOFS_END_TREE:
-               assert(obj->type == OBJ_TREE);
-               return LOFR_ZERO;
+               if (already_seen) {
+                       filter_res = LOFR_SKIP_TREE;
+               } else {
+                       int been_omitted = filter_trees_update_omits(
+                               obj, filter_data, include_it);
+                       seen_info->depth = filter_data->current_depth;
+
+                       if (include_it)
+                               filter_res = LOFR_DO_SHOW;
+                       else if (filter_data->omits && !been_omitted)
+                               /*
+                                * Must update omit information of children
+                                * recursively; they have not been omitted yet.
+                                */
+                               filter_res = LOFR_ZERO;
+                       else
+                               filter_res = LOFR_SKIP_TREE;
+               }
 
+               filter_data->current_depth++;
+               return filter_res;
        }
 }
 
-static void* filter_trees_none__init(
+static void filter_trees_free(void *filter_data) {
+       struct filter_trees_depth_data *d = filter_data;
+       if (!d)
+               return;
+       oidmap_free(&d->seen_at_depth, 1);
+       free(d);
+}
+
+static void *filter_trees_depth__init(
        struct oidset *omitted,
        struct list_objects_filter_options *filter_options,
        filter_object_fn *filter_fn,
        filter_free_fn *filter_free_fn)
 {
-       struct filter_trees_none_data *d = xcalloc(1, sizeof(*d));
+       struct filter_trees_depth_data *d = xcalloc(1, sizeof(*d));
        d->omits = omitted;
+       oidmap_init(&d->seen_at_depth, 0);
+       d->exclude_depth = filter_options->tree_exclude_depth;
+       d->current_depth = 0;
 
-       *filter_fn = filter_trees_none;
-       *filter_free_fn = free;
+       *filter_fn = filter_trees_depth;
+       *filter_free_fn = filter_trees_free;
        return d;
 }
 
@@ -144,6 +228,7 @@ struct filter_blobs_limit_data {
 };
 
 static enum list_objects_filter_result filter_blobs_limit(
+       struct repository *r,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
        const char *pathname,
@@ -171,7 +256,7 @@ static enum list_objects_filter_result filter_blobs_limit(
                assert(obj->type == OBJ_BLOB);
                assert((obj->flags & SEEN) == 0);
 
-               t = oid_object_info(the_repository, &obj->oid, &object_length);
+               t = oid_object_info(r, &obj->oid, &object_length);
                if (t != OBJ_BLOB) { /* probably OBJ_NONE */
                        /*
                         * We DO NOT have the blob locally, so we cannot
@@ -249,6 +334,7 @@ struct filter_sparse_data {
 };
 
 static enum list_objects_filter_result filter_sparse(
+       struct repository *r,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
        const char *pathname,
@@ -268,7 +354,7 @@ static enum list_objects_filter_result filter_sparse(
                dtype = DT_DIR;
                val = is_excluded_from_list(pathname, strlen(pathname),
                                            filename, &dtype, &filter_data->el,
-                                           &the_index);
+                                           r->index);
                if (val < 0)
                        val = filter_data->array_frame[filter_data->nr].defval;
 
@@ -331,7 +417,7 @@ static enum list_objects_filter_result filter_sparse(
                dtype = DT_REG;
                val = is_excluded_from_list(pathname, strlen(pathname),
                                            filename, &dtype, &filter_data->el,
-                                           &the_index);
+                                           r->index);
                if (val < 0)
                        val = frame->defval;
                if (val > 0) {
@@ -426,7 +512,7 @@ static filter_init_fn s_filters[] = {
        NULL,
        filter_blobs_none__init,
        filter_blobs_limit__init,
-       filter_trees_none__init,
+       filter_trees_depth__init,
        filter_sparse_oid__init,
        filter_sparse_path__init,
 };
index 52b4a84da9418497a4fc37e50cb8e0dcbc5b70bd..1d45a4ad5786c915f1fd597ce782cd96b7301532 100644 (file)
@@ -4,6 +4,7 @@
 struct list_objects_filter_options;
 struct object;
 struct oidset;
+struct repository;
 
 /*
  * During list-object traversal we allow certain objects to be
@@ -60,6 +61,7 @@ enum list_objects_filter_situation {
 };
 
 typedef enum list_objects_filter_result (*filter_object_fn)(
+       struct repository *r,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
        const char *pathname,
index c41cc80db5bc86279bcf560109f599a9783b2755..a2296a8e7b42a3d5d044c648d353ac94f9b4ed81 100644 (file)
@@ -55,7 +55,8 @@ static void process_blob(struct traversal_context *ctx,
        pathlen = path->len;
        strbuf_addstr(path, name);
        if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn)
-               r = ctx->filter_fn(LOFS_BLOB, obj,
+               r = ctx->filter_fn(ctx->revs->repo,
+                                  LOFS_BLOB, obj,
                                   path->buf, &path->buf[pathlen],
                                   ctx->filter_data);
        if (r & LOFR_MARK_SEEN)
@@ -113,7 +114,8 @@ static void process_tree_contents(struct traversal_context *ctx,
 
        while (tree_entry(&desc, &entry)) {
                if (match != all_entries_interesting) {
-                       match = tree_entry_interesting(&entry, base, 0,
+                       match = tree_entry_interesting(ctx->revs->repo->index,
+                                                      &entry, base, 0,
                                                       &ctx->revs->diffopt.pathspec);
                        if (match == all_entries_not_interesting)
                                break;
@@ -122,15 +124,15 @@ static void process_tree_contents(struct traversal_context *ctx,
                }
 
                if (S_ISDIR(entry.mode)) {
-                       struct tree *t = lookup_tree(the_repository, entry.oid);
+                       struct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);
                        t->object.flags |= NOT_USER_GIVEN;
                        process_tree(ctx, t, base, entry.path);
                }
                else if (S_ISGITLINK(entry.mode))
-                       process_gitlink(ctx, entry.oid->hash,
+                       process_gitlink(ctx, entry.oid.hash,
                                        base, entry.path);
                else {
-                       struct blob *b = lookup_blob(the_repository, entry.oid);
+                       struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);
                        b->object.flags |= NOT_USER_GIVEN;
                        process_blob(ctx, b, base, entry.path);
                }
@@ -175,7 +177,8 @@ static void process_tree(struct traversal_context *ctx,
 
        strbuf_addstr(base, name);
        if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn)
-               r = ctx->filter_fn(LOFS_BEGIN_TREE, obj,
+               r = ctx->filter_fn(ctx->revs->repo,
+                                  LOFS_BEGIN_TREE, obj,
                                   base->buf, &base->buf[baselen],
                                   ctx->filter_data);
        if (r & LOFR_MARK_SEEN)
@@ -191,7 +194,8 @@ static void process_tree(struct traversal_context *ctx,
                process_tree_contents(ctx, tree, base);
 
        if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn) {
-               r = ctx->filter_fn(LOFS_END_TREE, obj,
+               r = ctx->filter_fn(ctx->revs->repo,
+                                  LOFS_END_TREE, obj,
                                   base->buf, &base->buf[baselen],
                                   ctx->filter_data);
                if (r & LOFR_MARK_SEEN)
index 3c8fb917e9748b1950d5df8c85aa310149f1f8df..5b8d46aedee72aab8b027cf4310b4234268965b6 100644 (file)
@@ -384,7 +384,9 @@ int ll_merge(mmbuffer_t *result_buf,
        if (opts->virtual_ancestor) {
                if (driver->recursive)
                        driver = find_ll_merge_driver(driver->recursive);
-               marker_size += 2;
+       }
+       if (opts->extra_marker_size) {
+               marker_size += opts->extra_marker_size;
        }
        return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
                          ours, our_label, theirs, their_label,
index 6c6e22e40d9bab4b5071319acd5356993e7888b4..b9e2af1c884042adda8127a07ce8565b64303047 100644 (file)
@@ -13,6 +13,7 @@ struct ll_merge_options {
        unsigned virtual_ancestor : 1;
        unsigned variant : 2;   /* favor ours, favor theirs, or union merge */
        unsigned renormalize : 1;
+       unsigned extra_marker_size;
        long xdl_opts;
 };
 
index 10680c139eeb530bc0e70e0c1713767b6ca5be21..3cb14256ec5b7a2712e23b8feb12b1a91328bc34 100644 (file)
@@ -700,6 +700,7 @@ void show_log(struct rev_info *opt)
        ctx.color = opt->diffopt.use_color;
        ctx.expand_tabs_in_log = opt->expand_tabs_in_log;
        ctx.output_encoding = get_log_output_encoding();
+       ctx.rev = opt;
        if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
                ctx.from_ident = &opt->from_ident;
        if (opt->graph)
index a06f12eca8d6f9816c481f1f445791388d53039c..0a7dbc6442fad37fd9e2ce72866b04b134818181 100644 (file)
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -5,6 +5,7 @@
 #include "argv-array.h"
 #include "ls-refs.h"
 #include "pkt-line.h"
+#include "config.h"
 
 /*
  * Check if one of the prefixes is a prefix of the ref.
@@ -40,7 +41,10 @@ static int send_ref(const char *refname, const struct object_id *oid,
        const char *refname_nons = strip_namespace(refname);
        struct strbuf refline = STRBUF_INIT;
 
-       if (!ref_match(&data->prefixes, refname))
+       if (ref_is_hidden(refname_nons, refname))
+               return 0;
+
+       if (!ref_match(&data->prefixes, refname_nons))
                return 0;
 
        strbuf_addf(&refline, "%s %s", oid_to_hex(oid), refname_nons);
@@ -69,6 +73,16 @@ static int send_ref(const char *refname, const struct object_id *oid,
        return 0;
 }
 
+static int ls_refs_config(const char *var, const char *value, void *data)
+{
+       /*
+        * We only serve fetches over v2 for now, so respect only "uploadpack"
+        * config. This may need to eventually be expanded to "receive", but we
+        * don't yet know how that information will be passed to ls-refs.
+        */
+       return parse_hide_refs_config(var, value, "uploadpack");
+}
+
 int ls_refs(struct repository *r, struct argv_array *keys,
            struct packet_reader *request)
 {
@@ -76,6 +90,8 @@ int ls_refs(struct repository *r, struct argv_array *keys,
 
        memset(&data, 0, sizeof(data));
 
+       git_config(ls_refs_config, NULL);
+
        while (packet_reader_read(request) != PACKET_READ_FLUSH) {
                const char *arg = request->line;
                const char *out;
index 2b6d31ef9d35d9422e7ef90792651dcb7a2f9189..18ab825bef57f3ea63f70adf9a5c4e4a4f000bc4 100644 (file)
@@ -106,7 +106,7 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha
                        update_tree_entry(&two);
                } else {
                        /* path appears in both */
-                       if (!oideq(one.entry.oid, two.entry.oid)) {
+                       if (!oideq(&one.entry.oid, &two.entry.oid)) {
                                /* they are different */
                                score += score_differs(one.entry.mode,
                                                       two.entry.mode,
@@ -179,7 +179,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
        char *buf;
        unsigned long sz;
        struct tree_desc desc;
-       struct object_id *rewrite_here;
+       unsigned char *rewrite_here;
        const struct object_id *rewrite_with;
        struct object_id subtree;
        enum object_type type;
@@ -199,15 +199,26 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
        while (desc.size) {
                const char *name;
                unsigned mode;
-               const struct object_id *oid;
 
-               oid = tree_entry_extract(&desc, &name, &mode);
+               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,
                                    oid_to_hex(oid1));
-                       rewrite_here = (struct object_id *)oid;
+
+                       /*
+                        * We cast here for two reasons:
+                        *
+                        *   - to flip the "char *" (for the path) to "unsigned
+                        *     char *" (for the hash stored after it)
+                        *
+                        *   - to discard the "const"; this is OK because we
+                        *     know it points into our non-const "buf"
+                        */
+                       rewrite_here = (unsigned char *)(desc.entry.path +
+                                                        strlen(desc.entry.path) +
+                                                        1);
                        break;
                }
                update_tree_entry(&desc);
@@ -216,14 +227,16 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
                die("entry %.*s not found in tree %s", toplen, prefix,
                    oid_to_hex(oid1));
        if (*subpath) {
-               status = splice_tree(rewrite_here, subpath, oid2, &subtree);
+               struct object_id tree_oid;
+               hashcpy(tree_oid.hash, rewrite_here);
+               status = splice_tree(&tree_oid, subpath, oid2, &subtree);
                if (status)
                        return status;
                rewrite_with = &subtree;
        } else {
                rewrite_with = oid2;
        }
-       oidcpy(rewrite_here, rewrite_with);
+       hashcpy(rewrite_here, rewrite_with->hash);
        status = write_object_file(buf, sz, tree_type, result);
        free(buf);
        return status;
index acc2f64a4e9d03f49165e3c46396ce00a09dc6bd..59ba4b4a1a083b07050c5e6eba8306cb63736a3e 100644 (file)
@@ -186,6 +186,7 @@ static int oid_eq(const struct object_id *a, const struct object_id *b)
 enum rename_type {
        RENAME_NORMAL = 0,
        RENAME_VIA_DIR,
+       RENAME_ADD,
        RENAME_DELETE,
        RENAME_ONE_FILE_TO_ONE,
        RENAME_ONE_FILE_TO_TWO,
@@ -228,6 +229,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,
                                              struct stage_data *src_entry1,
                                              struct stage_data *src_entry2)
 {
+       int ostage1 = 0, ostage2;
        struct rename_conflict_info *ci;
 
        /*
@@ -264,18 +266,22 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,
                dst_entry2->rename_conflict_info = ci;
        }
 
-       if (rename_type == RENAME_TWO_FILES_TO_ONE) {
-               /*
-                * For each rename, there could have been
-                * modifications on the side of history where that
-                * file was not renamed.
-                */
-               int ostage1 = o->branch1 == branch1 ? 3 : 2;
-               int ostage2 = ostage1 ^ 1;
+       /*
+        * For each rename, there could have been
+        * modifications on the side of history where that
+        * file was not renamed.
+        */
+       if (rename_type == RENAME_ADD ||
+           rename_type == RENAME_TWO_FILES_TO_ONE) {
+               ostage1 = o->branch1 == branch1 ? 3 : 2;
 
                ci->ren1_other.path = pair1->one->path;
                oidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid);
                ci->ren1_other.mode = src_entry1->stages[ostage1].mode;
+       }
+
+       if (rename_type == RENAME_TWO_FILES_TO_ONE) {
+               ostage2 = ostage1 ^ 1;
 
                ci->ren2_other.path = pair2->one->path;
                oidcpy(&ci->ren2_other.oid, &src_entry2->stages[ostage2].oid);
@@ -463,7 +469,8 @@ static void get_files_dirs(struct merge_options *o, struct tree *tree)
 {
        struct pathspec match_all;
        memset(&match_all, 0, sizeof(match_all));
-       read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o);
+       read_tree_recursive(the_repository, tree, "", 0, 0,
+                           &match_all, save_files_dirs, o);
 }
 
 static int get_tree_entry_if_blob(const struct object_id *tree,
@@ -690,27 +697,6 @@ static int update_stages(struct merge_options *opt, const char *path,
        return 0;
 }
 
-static int update_stages_for_stage_data(struct merge_options *opt,
-                                       const char *path,
-                                       const struct stage_data *stage_data)
-{
-       struct diff_filespec o, a, b;
-
-       o.mode = stage_data->stages[1].mode;
-       oidcpy(&o.oid, &stage_data->stages[1].oid);
-
-       a.mode = stage_data->stages[2].mode;
-       oidcpy(&a.oid, &stage_data->stages[2].oid);
-
-       b.mode = stage_data->stages[3].mode;
-       oidcpy(&b.oid, &stage_data->stages[3].oid);
-
-       return update_stages(opt, path,
-                            is_null_oid(&o.oid) ? NULL : &o,
-                            is_null_oid(&a.oid) ? NULL : &a,
-                            is_null_oid(&b.oid) ? NULL : &b);
-}
-
 static void update_entry(struct stage_data *entry,
                         struct diff_filespec *o,
                         struct diff_filespec *a,
@@ -1058,7 +1044,8 @@ static int merge_3way(struct merge_options *o,
                      const struct diff_filespec *a,
                      const struct diff_filespec *b,
                      const char *branch1,
-                     const char *branch2)
+                     const char *branch2,
+                     const int extra_marker_size)
 {
        mmfile_t orig, src1, src2;
        struct ll_merge_options ll_opts = {0};
@@ -1066,6 +1053,7 @@ static int merge_3way(struct merge_options *o,
        int merge_status;
 
        ll_opts.renormalize = o->renormalize;
+       ll_opts.extra_marker_size = extra_marker_size;
        ll_opts.xdl_opts = o->xdl_opts;
 
        if (o->call_depth) {
@@ -1301,6 +1289,7 @@ static int merge_mode_and_contents(struct merge_options *o,
                                   const char *filename,
                                   const char *branch1,
                                   const char *branch2,
+                                  const int extra_marker_size,
                                   struct merge_file_info *result)
 {
        if (o->branch1 != branch1) {
@@ -1311,7 +1300,8 @@ static int merge_mode_and_contents(struct merge_options *o,
                 */
                return merge_mode_and_contents(o, one, b, a,
                                               filename,
-                                              branch2, branch1, result);
+                                              branch2, branch1,
+                                              extra_marker_size, result);
        }
 
        result->merge = 0;
@@ -1352,7 +1342,8 @@ static int merge_mode_and_contents(struct merge_options *o,
                        int ret = 0, merge_status;
 
                        merge_status = merge_3way(o, &result_buf, one, a, b,
-                                                 branch1, branch2);
+                                                 branch1, branch2,
+                                                 extra_marker_size);
 
                        if ((merge_status < 0) || !result_buf.ptr)
                                ret = err(o, _("Failed to execute internal merge"));
@@ -1555,80 +1546,203 @@ static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,
        return target;
 }
 
-static int handle_file(struct merge_options *o,
-                       struct diff_filespec *rename,
-                       int stage,
-                       struct rename_conflict_info *ci)
+static int handle_file_collision(struct merge_options *o,
+                                const char *collide_path,
+                                const char *prev_path1,
+                                const char *prev_path2,
+                                const char *branch1, const char *branch2,
+                                const struct object_id *a_oid,
+                                unsigned int a_mode,
+                                const struct object_id *b_oid,
+                                unsigned int b_mode)
 {
-       char *dst_name = rename->path;
-       struct stage_data *dst_entry;
-       const char *cur_branch, *other_branch;
-       struct diff_filespec other;
-       struct diff_filespec *add;
-       int ret;
+       struct merge_file_info mfi;
+       struct diff_filespec null, a, b;
+       char *alt_path = NULL;
+       const char *update_path = collide_path;
 
-       if (stage == 2) {
-               dst_entry = ci->dst_entry1;
-               cur_branch = ci->branch1;
-               other_branch = ci->branch2;
-       } else {
-               dst_entry = ci->dst_entry2;
-               cur_branch = ci->branch2;
-               other_branch = ci->branch1;
+       /*
+        * It's easiest to get the correct things into stage 2 and 3, and
+        * to make sure that the content merge puts HEAD before the other
+        * branch if we just ensure that branch1 == o->branch1.  So, simply
+        * flip arguments around if we don't have that.
+        */
+       if (branch1 != o->branch1) {
+               return handle_file_collision(o, collide_path,
+                                            prev_path2, prev_path1,
+                                            branch2, branch1,
+                                            b_oid, b_mode,
+                                            a_oid, a_mode);
        }
 
-       add = filespec_from_entry(&other, dst_entry, stage ^ 1);
-       if (add) {
-               int ren_src_was_dirty = was_dirty(o, rename->path);
-               char *add_name = unique_path(o, rename->path, other_branch);
-               if (update_file(o, 0, &add->oid, add->mode, add_name))
-                       return -1;
+       /*
+        * In the recursive case, we just opt to undo renames
+        */
+       if (o->call_depth && (prev_path1 || prev_path2)) {
+               /* Put first file (a_oid, a_mode) in its original spot */
+               if (prev_path1) {
+                       if (update_file(o, 1, a_oid, a_mode, prev_path1))
+                               return -1;
+               } else {
+                       if (update_file(o, 1, a_oid, a_mode, collide_path))
+                               return -1;
+               }
 
-               if (ren_src_was_dirty) {
-                       output(o, 1, _("Refusing to lose dirty file at %s"),
-                              rename->path);
+               /* Put second file (b_oid, b_mode) in its original spot */
+               if (prev_path2) {
+                       if (update_file(o, 1, b_oid, b_mode, prev_path2))
+                               return -1;
+               } else {
+                       if (update_file(o, 1, b_oid, b_mode, collide_path))
+                               return -1;
                }
+
+               /* Don't leave something at collision path if unrenaming both */
+               if (prev_path1 && prev_path2)
+                       remove_file(o, 1, collide_path, 0);
+
+               return 0;
+       }
+
+       /* Remove rename sources if rename/add or rename/rename(2to1) */
+       if (prev_path1)
+               remove_file(o, 1, prev_path1,
+                           o->call_depth || would_lose_untracked(prev_path1));
+       if (prev_path2)
+               remove_file(o, 1, prev_path2,
+                           o->call_depth || would_lose_untracked(prev_path2));
+
+       /*
+        * Remove the collision path, if it wouldn't cause dirty contents
+        * or an untracked file to get lost.  We'll either overwrite with
+        * merged contents, or just write out to differently named files.
+        */
+       if (was_dirty(o, collide_path)) {
+               output(o, 1, _("Refusing to lose dirty file at %s"),
+                      collide_path);
+               update_path = alt_path = unique_path(o, collide_path, "merged");
+       } else if (would_lose_untracked(collide_path)) {
                /*
-                * Because the double negatives somehow keep confusing me...
-                *    1) update_wd iff !ren_src_was_dirty.
-                *    2) no_wd iff !update_wd
-                *    3) so, no_wd == !!ren_src_was_dirty == ren_src_was_dirty
+                * Only way we get here is if both renames were from
+                * a directory rename AND user had an untracked file
+                * at the location where both files end up after the
+                * two directory renames.  See testcase 10d of t6043.
                 */
-               remove_file(o, 0, rename->path, ren_src_was_dirty);
-               dst_name = unique_path(o, rename->path, cur_branch);
+               output(o, 1, _("Refusing to lose untracked file at "
+                              "%s, even though it's in the way."),
+                      collide_path);
+               update_path = alt_path = unique_path(o, collide_path, "merged");
        } else {
-               if (dir_in_way(rename->path, !o->call_depth, 0)) {
-                       dst_name = unique_path(o, rename->path, cur_branch);
-                       output(o, 1, _("%s is a directory in %s adding as %s instead"),
-                              rename->path, other_branch, dst_name);
-               } else if (!o->call_depth &&
-                          would_lose_untracked(rename->path)) {
-                       dst_name = unique_path(o, rename->path, cur_branch);
-                       output(o, 1, _("Refusing to lose untracked file at %s; "
-                                      "adding as %s instead"),
-                              rename->path, dst_name);
-               }
+               /*
+                * FIXME: It's possible that the two files are identical
+                * and that the current working copy happens to match, in
+                * which case we are unnecessarily touching the working
+                * tree file.  It's not a likely enough scenario that I
+                * want to code up the checks for it and a better fix is
+                * available if we restructure how unpack_trees() and
+                * merge-recursive interoperate anyway, so punting for
+                * now...
+                */
+               remove_file(o, 0, collide_path, 0);
        }
-       if ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))
-               ; /* fall through, do allow dst_name to be released */
-       else if (stage == 2)
-               ret = update_stages(o, rename->path, NULL, rename, add);
-       else
-               ret = update_stages(o, rename->path, NULL, add, rename);
 
-       if (dst_name != rename->path)
-               free(dst_name);
+       /* Store things in diff_filespecs for functions that need it */
+       memset(&a, 0, sizeof(struct diff_filespec));
+       memset(&b, 0, sizeof(struct diff_filespec));
+       null.path = a.path = b.path = (char *)collide_path;
+       oidcpy(&null.oid, &null_oid);
+       null.mode = 0;
+       oidcpy(&a.oid, a_oid);
+       a.mode = a_mode;
+       a.oid_valid = 1;
+       oidcpy(&b.oid, b_oid);
+       b.mode = b_mode;
+       b.oid_valid = 1;
 
-       return ret;
+       if (merge_mode_and_contents(o, &null, &a, &b, collide_path,
+                                   branch1, branch2, o->call_depth * 2, &mfi))
+               return -1;
+       mfi.clean &= !alt_path;
+       if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, update_path))
+               return -1;
+       if (!mfi.clean && !o->call_depth &&
+           update_stages(o, collide_path, NULL, &a, &b))
+               return -1;
+       free(alt_path);
+       /*
+        * FIXME: If both a & b both started with conflicts (only possible
+        * if they came from a rename/rename(2to1)), but had IDENTICAL
+        * contents including those conflicts, then in the next line we claim
+        * it was clean.  If someone cares about this case, we should have the
+        * caller notify us if we started with conflicts.
+        */
+       return mfi.clean;
+}
+
+static int handle_rename_add(struct merge_options *o,
+                            struct rename_conflict_info *ci)
+{
+       /* a was renamed to c, and a separate c was added. */
+       struct diff_filespec *a = ci->pair1->one;
+       struct diff_filespec *c = ci->pair1->two;
+       char *path = c->path;
+       char *prev_path_desc;
+       struct merge_file_info mfi;
+
+       int other_stage = (ci->branch1 == o->branch1 ? 3 : 2);
+
+       output(o, 1, _("CONFLICT (rename/add): "
+              "Rename %s->%s in %s.  Added %s in %s"),
+              a->path, c->path, ci->branch1,
+              c->path, ci->branch2);
+
+       prev_path_desc = xstrfmt("version of %s from %s", path, a->path);
+       if (merge_mode_and_contents(o, a, c, &ci->ren1_other, prev_path_desc,
+                                   o->branch1, o->branch2,
+                                   1 + o->call_depth * 2, &mfi))
+               return -1;
+       free(prev_path_desc);
+
+       return handle_file_collision(o,
+                                    c->path, a->path, NULL,
+                                    ci->branch1, ci->branch2,
+                                    &mfi.oid, mfi.mode,
+                                    &ci->dst_entry1->stages[other_stage].oid,
+                                    ci->dst_entry1->stages[other_stage].mode);
+}
+
+static char *find_path_for_conflict(struct merge_options *o,
+                                   const char *path,
+                                   const char *branch1,
+                                   const char *branch2)
+{
+       char *new_path = NULL;
+       if (dir_in_way(path, !o->call_depth, 0)) {
+               new_path = unique_path(o, path, branch1);
+               output(o, 1, _("%s is a directory in %s adding "
+                              "as %s instead"),
+                      path, branch2, new_path);
+       } else if (would_lose_untracked(path)) {
+               new_path = unique_path(o, path, branch1);
+               output(o, 1, _("Refusing to lose untracked file"
+                              " at %s; adding as %s instead"),
+                      path, new_path);
+       }
+
+       return new_path;
 }
 
 static int handle_rename_rename_1to2(struct merge_options *o,
                                     struct rename_conflict_info *ci)
 {
        /* One file was renamed in both branches, but to different names. */
+       struct merge_file_info mfi;
+       struct diff_filespec other;
+       struct diff_filespec *add;
        struct diff_filespec *one = ci->pair1->one;
        struct diff_filespec *a = ci->pair1->two;
        struct diff_filespec *b = ci->pair2->two;
+       char *path_desc;
 
        output(o, 1, _("CONFLICT (rename/rename): "
               "Rename \"%s\"->\"%s\" in branch \"%s\" "
@@ -1636,14 +1750,16 @@ static int handle_rename_rename_1to2(struct merge_options *o,
               one->path, a->path, ci->branch1,
               one->path, b->path, ci->branch2,
               o->call_depth ? _(" (left unresolved)") : "");
-       if (o->call_depth) {
-               struct merge_file_info mfi;
-               struct diff_filespec other;
-               struct diff_filespec *add;
-               if (merge_mode_and_contents(o, one, a, b, one->path,
-                                           ci->branch1, ci->branch2, &mfi))
-                       return -1;
 
+       path_desc = xstrfmt("%s and %s, both renamed from %s",
+                           a->path, b->path, one->path);
+       if (merge_mode_and_contents(o, one, a, b, path_desc,
+                                   ci->branch1, ci->branch2,
+                                   o->call_depth * 2, &mfi))
+               return -1;
+       free(path_desc);
+
+       if (o->call_depth) {
                /*
                 * FIXME: For rename/add-source conflicts (if we could detect
                 * such), this is wrong.  We should instead find a unique
@@ -1675,8 +1791,50 @@ static int handle_rename_rename_1to2(struct merge_options *o,
                }
                else
                        remove_file_from_cache(b->path);
-       } else if (handle_file(o, a, 2, ci) || handle_file(o, b, 3, ci))
-               return -1;
+       } else {
+               /*
+                * For each destination path, we need to see if there is a
+                * rename/add collision.  If not, we can write the file out
+                * to the specified location.
+                */
+               add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);
+               if (add) {
+                       if (handle_file_collision(o, a->path,
+                                                 NULL, NULL,
+                                                 ci->branch1, ci->branch2,
+                                                 &mfi.oid, mfi.mode,
+                                                 &add->oid, add->mode) < 0)
+                               return -1;
+               } else {
+                       char *new_path = find_path_for_conflict(o, a->path,
+                                                               ci->branch1,
+                                                               ci->branch2);
+                       if (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))
+                               return -1;
+                       free(new_path);
+                       if (update_stages(o, a->path, NULL, a, NULL))
+                               return -1;
+               }
+
+               add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);
+               if (add) {
+                       if (handle_file_collision(o, b->path,
+                                                 NULL, NULL,
+                                                 ci->branch1, ci->branch2,
+                                                 &add->oid, add->mode,
+                                                 &mfi.oid, mfi.mode) < 0)
+                               return -1;
+               } else {
+                       char *new_path = find_path_for_conflict(o, b->path,
+                                                               ci->branch2,
+                                                               ci->branch1);
+                       if (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))
+                               return -1;
+                       free(new_path);
+                       if (update_stages(o, b->path, NULL, NULL, b))
+                               return -1;
+               }
+       }
 
        return 0;
 }
@@ -1694,7 +1852,6 @@ static int handle_rename_rename_2to1(struct merge_options *o,
        char *path_side_2_desc;
        struct merge_file_info mfi_c1;
        struct merge_file_info mfi_c2;
-       int ret;
 
        output(o, 1, _("CONFLICT (rename/rename): "
               "Rename %s->%s in %s. "
@@ -1702,79 +1859,22 @@ static int handle_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, o->call_depth || would_lose_untracked(a->path));
-       remove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));
-
        path_side_1_desc = xstrfmt("version of %s from %s", path, a->path);
        path_side_2_desc = xstrfmt("version of %s from %s", path, b->path);
        if (merge_mode_and_contents(o, a, c1, &ci->ren1_other, path_side_1_desc,
-                                   o->branch1, o->branch2, &mfi_c1) ||
+                                   o->branch1, o->branch2,
+                                   1 + o->call_depth * 2, &mfi_c1) ||
            merge_mode_and_contents(o, b, &ci->ren2_other, c2, path_side_2_desc,
-                                   o->branch1, o->branch2, &mfi_c2))
+                                   o->branch1, o->branch2,
+                                   1 + o->call_depth * 2, &mfi_c2))
                return -1;
        free(path_side_1_desc);
        free(path_side_2_desc);
 
-       if (o->call_depth) {
-               /*
-                * If mfi_c1.clean && mfi_c2.clean, then it might make
-                * sense to do a two-way merge of those results.  But, I
-                * think in all cases, it makes sense to have the virtual
-                * merge base just undo the renames; they can be detected
-                * again later for the non-recursive merge.
-                */
-               remove_file(o, 0, path, 0);
-               ret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);
-               if (!ret)
-                       ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,
-                                         b->path);
-       } else {
-               char *new_path1 = unique_path(o, path, ci->branch1);
-               char *new_path2 = unique_path(o, path, ci->branch2);
-               output(o, 1, _("Renaming %s to %s and %s to %s instead"),
-                      a->path, new_path1, b->path, new_path2);
-               if (was_dirty(o, path))
-                       output(o, 1, _("Refusing to lose dirty file at %s"),
-                              path);
-               else if (would_lose_untracked(path))
-                       /*
-                        * Only way we get here is if both renames were from
-                        * a directory rename AND user had an untracked file
-                        * at the location where both files end up after the
-                        * two directory renames.  See testcase 10d of t6043.
-                        */
-                       output(o, 1, _("Refusing to lose untracked file at "
-                                      "%s, even though it's in the way."),
-                              path);
-               else
-                       remove_file(o, 0, path, 0);
-               ret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);
-               if (!ret)
-                       ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,
-                                         new_path2);
-               /*
-                * unpack_trees() actually populates the index for us for
-                * "normal" rename/rename(2to1) situtations so that the
-                * correct entries are at the higher stages, which would
-                * make the call below to update_stages_for_stage_data
-                * unnecessary.  However, if either of the renames came
-                * from a directory rename, then unpack_trees() will not
-                * have gotten the right data loaded into the index, so we
-                * need to do so now.  (While it'd be tempting to move this
-                * call to update_stages_for_stage_data() to
-                * apply_directory_rename_modifications(), that would break
-                * our intermediate calls to would_lose_untracked() since
-                * those rely on the current in-memory index.  See also the
-                * big "NOTE" in update_stages()).
-                */
-               if (update_stages_for_stage_data(o, path, ci->dst_entry1))
-                       ret = -1;
-
-               free(new_path2);
-               free(new_path1);
-       }
-
-       return ret;
+       return handle_file_collision(o, path, a->path, b->path,
+                                    ci->branch1, ci->branch2,
+                                    &mfi_c1.oid, mfi_c1.mode,
+                                    &mfi_c2.oid, mfi_c2.mode);
 }
 
 /*
@@ -2732,47 +2832,23 @@ static int process_renames(struct merge_options *o,
                                                      0  /* update_wd    */))
                                        clean_merge = -1;
                        } else if (!oid_eq(&dst_other.oid, &null_oid)) {
-                               clean_merge = 0;
-                               try_merge = 1;
-                               output(o, 1, _("CONFLICT (rename/add): Rename %s->%s in %s. "
-                                      "%s added in %s"),
-                                      ren1_src, ren1_dst, branch1,
-                                      ren1_dst, branch2);
-                               if (o->call_depth) {
-                                       struct merge_file_info mfi;
-                                       struct diff_filespec one, a, b;
-
-                                       oidcpy(&one.oid, &null_oid);
-                                       one.mode = 0;
-                                       one.path = ren1->pair->two->path;
-
-                                       oidcpy(&a.oid, &ren1->pair->two->oid);
-                                       a.mode = ren1->pair->two->mode;
-                                       a.path = one.path;
-
-                                       oidcpy(&b.oid, &dst_other.oid);
-                                       b.mode = dst_other.mode;
-                                       b.path = one.path;
-
-                                       if (merge_mode_and_contents(o, &one, &a, &b, ren1_dst,
-                                                                   branch1, branch2,
-                                                                   &mfi)) {
-                                               clean_merge = -1;
-                                               goto cleanup_and_return;
-                                       }
-                                       output(o, 1, _("Adding merged %s"), ren1_dst);
-                                       if (update_file(o, 0, &mfi.oid,
-                                                       mfi.mode, ren1_dst))
-                                               clean_merge = -1;
-                                       try_merge = 0;
-                               } else {
-                                       char *new_path = unique_path(o, ren1_dst, branch2);
-                                       output(o, 1, _("Adding as %s instead"), new_path);
-                                       if (update_file(o, 0, &dst_other.oid,
-                                                       dst_other.mode, new_path))
-                                               clean_merge = -1;
-                                       free(new_path);
-                               }
+                               /*
+                                * Probably not a clean merge, but it's
+                                * premature to set clean_merge to 0 here,
+                                * because if the rename merges cleanly and
+                                * the merge exactly matches the newly added
+                                * file, then the merge will be clean.
+                                */
+                               setup_rename_conflict_info(RENAME_ADD,
+                                                          ren1->pair,
+                                                          NULL,
+                                                          branch1,
+                                                          branch2,
+                                                          ren1->dst_entry,
+                                                          NULL,
+                                                          o,
+                                                          ren1->src_entry,
+                                                          NULL);
                        } else
                                try_merge = 1;
 
@@ -3053,7 +3129,8 @@ static int handle_content_merge(struct merge_options *o,
                        df_conflict_remains = 1;
        }
        if (merge_mode_and_contents(o, &one, &a, &b, path,
-                                   o->branch1, o->branch2, &mfi))
+                                   o->branch1, o->branch2,
+                                   o->call_depth * 2, &mfi))
                return -1;
 
        /*
@@ -3183,6 +3260,15 @@ static int process_entry(struct merge_options *o,
                                                  conflict_info->branch2))
                                clean_merge = -1;
                        break;
+               case RENAME_ADD:
+                       /*
+                        * Probably unclean merge, but if the renamed file
+                        * merges cleanly and the result can then be
+                        * two-way merged cleanly with the added file, I
+                        * guess it's a clean merge?
+                        */
+                       clean_merge = handle_rename_add(o, conflict_info);
+                       break;
                case RENAME_DELETE:
                        clean_merge = 0;
                        if (handle_rename_delete(o,
@@ -3197,9 +3283,14 @@ static int process_entry(struct merge_options *o,
                                clean_merge = -1;
                        break;
                case RENAME_TWO_FILES_TO_ONE:
-                       clean_merge = 0;
-                       if (handle_rename_rename_2to1(o, conflict_info))
-                               clean_merge = -1;
+                       /*
+                        * Probably unclean merge, but if the two renamed
+                        * files merge cleanly and the two resulting files
+                        * can then be two-way merged cleanly, I guess it's
+                        * a clean merge?
+                        */
+                       clean_merge = handle_rename_rename_2to1(o,
+                                                               conflict_info);
                        break;
                default:
                        entry->processed = 0;
@@ -3267,14 +3358,27 @@ static int process_entry(struct merge_options *o,
                                clean_merge = -1;
                }
        } else if (a_oid && b_oid) {
-               /* Case C: Added in both (check for same permissions) and */
-               /* case D: Modified in both, but differently. */
-               int is_dirty = 0; /* unpack_trees would have bailed if dirty */
-               clean_merge = handle_content_merge(o, path, is_dirty,
-                                                  o_oid, o_mode,
-                                                  a_oid, a_mode,
-                                                  b_oid, b_mode,
-                                                  NULL);
+               if (!o_oid) {
+                       /* Case C: Added in both (check for same permissions) */
+                       output(o, 1,
+                              _("CONFLICT (add/add): Merge conflict in %s"),
+                              path);
+                       clean_merge = handle_file_collision(o,
+                                                           path, NULL, NULL,
+                                                           o->branch1,
+                                                           o->branch2,
+                                                           a_oid, a_mode,
+                                                           b_oid, b_mode);
+               } else {
+                       /* case D: Modified in both, but differently. */
+                       int is_dirty = 0; /* unpack_trees would have bailed if dirty */
+                       clean_merge = handle_content_merge(o, path,
+                                                          is_dirty,
+                                                          o_oid, o_mode,
+                                                          a_oid, a_mode,
+                                                          b_oid, b_mode,
+                                                          NULL);
+               }
        } else if (!o_oid && !a_oid && !b_oid) {
                /*
                 * this entry was deleted altogether. a_mode == 0 means
diff --git a/midx.c b/midx.c
index 2a6a24fcd7eff1a074c37e6c556f2143ef134c19..8a505fd423efccf9986079542b47967cc93df7dc 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -931,7 +931,7 @@ cleanup:
 
 void clear_midx_file(struct repository *r)
 {
-       char *midx = get_midx_filename(r->objects->objectdir);
+       char *midx = get_midx_filename(r->objects->odb->path);
 
        if (r->objects && r->objects->multi_pack_index) {
                close_midx(r->objects->multi_pack_index);
index d87e7ca91cdecb158ca93659fb58968e92b18b31..2473314d686858493c6730e895e7106e44a462f9 100644 (file)
@@ -5,7 +5,9 @@
 #include "commit.h"
 #include "refs.h"
 
-static int notes_cache_match_validity(const char *ref, const char *validity)
+static int notes_cache_match_validity(struct repository *r,
+                                     const char *ref,
+                                     const char *validity)
 {
        struct object_id oid;
        struct commit *commit;
@@ -16,7 +18,7 @@ static int notes_cache_match_validity(const char *ref, const char *validity)
        if (read_ref(ref, &oid) < 0)
                return 0;
 
-       commit = lookup_commit_reference_gently(the_repository, &oid, 1);
+       commit = lookup_commit_reference_gently(r, &oid, 1);
        if (!commit)
                return 0;
 
@@ -30,8 +32,8 @@ static int notes_cache_match_validity(const char *ref, const char *validity)
        return ret;
 }
 
-void notes_cache_init(struct notes_cache *c, const char *name,
-                    const char *validity)
+void notes_cache_init(struct repository *r, struct notes_cache *c,
+                     const char *name, const char *validity)
 {
        struct strbuf ref = STRBUF_INIT;
        int flags = NOTES_INIT_WRITABLE;
@@ -40,7 +42,7 @@ void notes_cache_init(struct notes_cache *c, const char *name,
        c->validity = xstrdup(validity);
 
        strbuf_addf(&ref, "refs/notes/%s", name);
-       if (!notes_cache_match_validity(ref.buf, validity))
+       if (!notes_cache_match_validity(r, ref.buf, validity))
                flags |= NOTES_INIT_EMPTY;
        init_notes(&c->tree, ref.buf, combine_notes_overwrite, flags);
        strbuf_release(&ref);
index aeeee8409d4320644a1f618f5cd25b815be52f1c..56f8c98e2445f036b1c3897dce976f419372cb61 100644 (file)
@@ -3,13 +3,15 @@
 
 #include "notes.h"
 
+struct repository;
+
 struct notes_cache {
        struct notes_tree tree;
        char *validity;
 };
 
-void notes_cache_init(struct notes_cache *c, const char *name,
-                    const char *validity);
+void notes_cache_init(struct repository *r, struct notes_cache *c,
+                     const char *name, const char *validity);
 int notes_cache_write(struct notes_cache *c);
 
 char *notes_cache_get(struct notes_cache *c, struct object_id *oid, size_t
index bd05d50b051288cb466de5afed52993a1edf3b1d..72688d301b7dd41ed1aa8ab5d3e5e61a6f4054c5 100644 (file)
@@ -18,11 +18,13 @@ struct notes_merge_pair {
        struct object_id obj, base, local, remote;
 };
 
-void init_notes_merge_options(struct notes_merge_options *o)
+void init_notes_merge_options(struct repository *r,
+                             struct notes_merge_options *o)
 {
        memset(o, 0, sizeof(struct notes_merge_options));
        strbuf_init(&(o->commit_msg), 0);
        o->verbosity = NOTES_MERGE_VERBOSITY_DEFAULT;
+       o->repo = r;
 }
 
 static int path_to_oid(const char *path, struct object_id *oid)
@@ -127,7 +129,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
        trace_printf("\tdiff_tree_remote(base = %.7s, remote = %.7s)\n",
               oid_to_hex(base), oid_to_hex(remote));
 
-       repo_diff_setup(the_repository, &opt);
+       repo_diff_setup(o->repo, &opt);
        opt.flags.recursive = 1;
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&opt);
@@ -190,7 +192,7 @@ static void diff_tree_local(struct notes_merge_options *o,
        trace_printf("\tdiff_tree_local(len = %i, base = %.7s, local = %.7s)\n",
               len, oid_to_hex(base), oid_to_hex(local));
 
-       repo_diff_setup(the_repository, &opt);
+       repo_diff_setup(o->repo, &opt);
        opt.flags.recursive = 1;
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&opt);
@@ -350,7 +352,7 @@ static int ll_merge_in_worktree(struct notes_merge_options *o,
 
        status = ll_merge(&result_buf, oid_to_hex(&p->obj), &base, NULL,
                          &local, o->local_ref, &remote, o->remote_ref,
-                         &the_index, NULL);
+                         o->repo->index, NULL);
 
        free(base.ptr);
        free(local.ptr);
@@ -556,7 +558,7 @@ int notes_merge(struct notes_merge_options *o,
        else if (!check_refname_format(o->local_ref, 0) &&
                is_null_oid(&local_oid))
                local = NULL; /* local_oid == null_oid indicates unborn ref */
-       else if (!(local = lookup_commit_reference(the_repository, &local_oid)))
+       else if (!(local = lookup_commit_reference(o->repo, &local_oid)))
                die("Could not parse local commit %s (%s)",
                    oid_to_hex(&local_oid), o->local_ref);
        trace_printf("\tlocal commit: %.7s\n", oid_to_hex(&local_oid));
@@ -574,7 +576,7 @@ int notes_merge(struct notes_merge_options *o,
                        die("Failed to resolve remote notes ref '%s'",
                            o->remote_ref);
                }
-       } else if (!(remote = lookup_commit_reference(the_repository, &remote_oid))) {
+       } else if (!(remote = lookup_commit_reference(o->repo, &remote_oid))) {
                die("Could not parse remote commit %s (%s)",
                    oid_to_hex(&remote_oid), o->remote_ref);
        }
@@ -711,7 +713,7 @@ int notes_merge_commit(struct notes_merge_options *o,
                /* write file as blob, and add to partial_tree */
                if (stat(path.buf, &st))
                        die_errno("Failed to stat '%s'", path.buf);
-               if (index_path(&the_index, &blob_oid, path.buf, &st, HASH_WRITE_OBJECT))
+               if (index_path(o->repo->index, &blob_oid, path.buf, &st, HASH_WRITE_OBJECT))
                        die("Failed to write blob object from '%s'", path.buf);
                if (add_note(partial_tree, &obj_oid, &blob_oid, NULL))
                        die("Failed to add resolved note '%s' to notes tree",
index 6c74e9385b83ce317d6ca91f328c7790d0753e2c..99f9c709c536ac19c677e97348e9924c2764d217 100644 (file)
@@ -6,6 +6,7 @@
 
 struct commit;
 struct object_id;
+struct repository;
 
 #define NOTES_MERGE_WORKTREE "NOTES_MERGE_WORKTREE"
 
@@ -15,6 +16,7 @@ enum notes_merge_verbosity {
 };
 
 struct notes_merge_options {
+       struct repository *repo;
        const char *local_ref;
        const char *remote_ref;
        struct strbuf commit_msg;
@@ -23,7 +25,8 @@ struct notes_merge_options {
        unsigned has_worktree:1;
 };
 
-void init_notes_merge_options(struct notes_merge_options *o);
+void init_notes_merge_options(struct repository *r,
+                             struct notes_merge_options *o);
 
 /*
  * Merge notes from o->remote_ref into o->local_ref
diff --git a/notes.c b/notes.c
index 25cdce28b71a3ff15da424c8d1b1a89e28356dd5..7f7cc4d5112cd8f5098477d03034e8c8886dcb84 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -450,7 +450,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
 
                l = xcalloc(1, sizeof(*l));
                oidcpy(&l->key_oid, &object_oid);
-               oidcpy(&l->val_oid, entry.oid);
+               oidcpy(&l->val_oid, &entry.oid);
                if (note_tree_insert(t, node, n, l, type,
                                     combine_notes_concatenate))
                        die("Failed to load %s %s into notes tree "
@@ -481,7 +481,7 @@ handle_non_note:
                        }
                        strbuf_addstr(&non_note_path, entry.path);
                        add_non_note(t, strbuf_detach(&non_note_path, NULL),
-                                    entry.mode, entry.oid->hash);
+                                    entry.mode, entry.oid.hash);
                }
        }
        free(buf);
index 63b7605a3e0b3000641d8f39f22e1d5c8139f19b..ba57630677fac412c6f8a771ed64eccf6000e833 100644 (file)
@@ -7,40 +7,33 @@
 #include "sha1-array.h"
 #include "strbuf.h"
 
-struct alternate_object_database {
-       struct alternate_object_database *next;
-
-       /* see alt_scratch_buf() */
-       struct strbuf scratch;
-       size_t base_len;
+struct object_directory {
+       struct object_directory *next;
 
        /*
-        * Used to store the results of readdir(3) calls when searching
-        * for unique abbreviated hashes.  This cache is never
-        * invalidated, thus it's racy and not necessarily accurate.
-        * That's fine for its purpose; don't use it for tasks requiring
-        * greater accuracy!
+        * Used to store the results of readdir(3) calls when we are OK
+        * sacrificing accuracy due to races for speed. That includes
+        * object existence with OBJECT_INFO_QUICK, as well as
+        * our search for unique abbreviated hashes. Don't use it for tasks
+        * requiring greater accuracy!
+        *
+        * Be sure to call odb_load_loose_cache() before using.
         */
        char loose_objects_subdir_seen[256];
-       struct oid_array loose_objects_cache;
+       struct oid_array loose_objects_cache[256];
 
        /*
         * Path to the alternative object store. If this is a relative path,
         * it is relative to the current working directory.
         */
-       char path[FLEX_ARRAY];
+       char *path;
 };
+
 void prepare_alt_odb(struct repository *r);
 char *compute_alternate_path(const char *path, struct strbuf *err);
-typedef int alt_odb_fn(struct alternate_object_database *, void *);
+typedef int alt_odb_fn(struct object_directory *, void *);
 int foreach_alt_odb(alt_odb_fn, void*);
 
-/*
- * Allocate a "struct alternate_object_database" but do _not_ actually
- * add it to the list of alternates.
- */
-struct alternate_object_database *alloc_alt_odb(const char *dir);
-
 /*
  * Add the directory to the on-disk alternates file; the new entry will also
  * take effect in the current process.
@@ -55,12 +48,14 @@ void add_to_alternates_file(const char *dir);
 void add_to_alternates_memory(const char *dir);
 
 /*
- * Returns a scratch strbuf pre-filled with the alternate object directory,
- * including a trailing slash, which can be used to access paths in the
- * alternate. Always use this over direct access to alt->scratch, as it
- * cleans up any previous use of the scratch buffer.
+ * Populate and return the loose object cache array corresponding to the
+ * given object ID.
  */
-struct strbuf *alt_scratch_buf(struct alternate_object_database *alt);
+struct oid_array *odb_loose_cache(struct object_directory *odb,
+                                 const struct object_id *oid);
+
+/* Empty the loose object cache for the specified object directory. */
+void odb_clear_loose_cache(struct object_directory *odb);
 
 struct packed_git {
        struct packed_git *next;
@@ -92,17 +87,21 @@ struct multi_pack_index;
 
 struct raw_object_store {
        /*
-        * Path to the repository's object store.
-        * Cannot be NULL after initialization.
+        * Set of all object directories; the main directory is first (and
+        * cannot be NULL after initialization). Subsequent directories are
+        * alternates.
         */
-       char *objectdir;
+       struct object_directory *odb;
+       struct object_directory **odb_tail;
+       int loaded_alternates;
 
-       /* Path to extra alternate object database if not NULL */
+       /*
+        * A list of alternate object directories loaded from the environment;
+        * this should not generally need to be accessed directly, but will
+        * populate the "odb" list when prepare_alt_odb() is run.
+        */
        char *alternate_db;
 
-       struct alternate_object_database *alt_odb_list;
-       struct alternate_object_database **alt_odb_tail;
-
        /*
         * Objects that should be substituted by other objects
         * (see git-replace(1)).
@@ -157,17 +156,24 @@ void raw_object_store_clear(struct raw_object_store *o);
  * Put in `buf` the name of the file in the local object database that
  * would be used to store a loose object with the specified sha1.
  */
-void sha1_file_name(struct repository *r, struct strbuf *buf, const unsigned char *sha1);
+const char *loose_object_path(struct repository *r, struct strbuf *buf, const unsigned char *sha1);
 
 void *map_sha1_file(struct repository *r, const unsigned char *sha1, unsigned long *size);
 
-extern void *read_object_file_extended(const struct object_id *oid,
+extern void *read_object_file_extended(struct repository *r,
+                                      const struct object_id *oid,
                                       enum object_type *type,
                                       unsigned long *size, int lookup_replace);
-static inline void *read_object_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
+static inline void *repo_read_object_file(struct repository *r,
+                                         const struct object_id *oid,
+                                         enum object_type *type,
+                                         unsigned long *size)
 {
-       return read_object_file_extended(oid, type, size, 1);
+       return read_object_file_extended(r, oid, type, size, 1);
 }
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define read_object_file(oid, type, size) repo_read_object_file(the_repository, oid, type, size)
+#endif
 
 /* Read and unpack an object file into memory, write memory to an object file */
 int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
@@ -205,15 +211,27 @@ int read_loose_object(const char *path,
  * object_info. OBJECT_INFO_SKIP_CACHED is automatically set; pass
  * nonzero flags to also set other flags.
  */
-extern int has_sha1_file_with_flags(const unsigned char *sha1, int flags);
-static inline int has_sha1_file(const unsigned char *sha1)
+int repo_has_sha1_file_with_flags(struct repository *r,
+                                 const unsigned char *sha1, int flags);
+static inline int repo_has_sha1_file(struct repository *r,
+                                    const unsigned char *sha1)
 {
-       return has_sha1_file_with_flags(sha1, 0);
+       return repo_has_sha1_file_with_flags(r, sha1, 0);
 }
 
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define has_sha1_file_with_flags(sha1, flags) repo_has_sha1_file_with_flags(the_repository, sha1, flags)
+#define has_sha1_file(sha1) repo_has_sha1_file(the_repository, sha1)
+#endif
+
 /* Same as the above, except for struct object_id. */
-extern int has_object_file(const struct object_id *oid);
-extern int has_object_file_with_flags(const struct object_id *oid, int flags);
+int repo_has_object_file(struct repository *r, const struct object_id *oid);
+int repo_has_object_file_with_flags(struct repository *r,
+                                   const struct object_id *oid, int flags);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define has_object_file(oid) repo_has_object_file(the_repository, oid)
+#define has_object_file_with_flags(oid, flags) repo_has_object_file_with_flags(the_repository, oid, flags)
+#endif
 
 /*
  * Return true iff an alternate object database has a loose object
index e54160550c5a8e864f2e2bbcc58a29ff73045f8f..5dc5eec367354ae4817b813b181423e096179a8a 100644 (file)
--- a/object.c
+++ b/object.c
@@ -164,8 +164,9 @@ void *object_as_type(struct repository *r, struct object *obj, enum object_type
                return obj;
        else if (obj->type == OBJ_NONE) {
                if (type == OBJ_COMMIT)
-                       ((struct commit *)obj)->index = alloc_commit_index(r);
-               obj->type = type;
+                       init_commit_node(r, (struct commit *) obj);
+               else
+                       obj->type = type;
                return obj;
        }
        else {
@@ -259,8 +260,8 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
        if (obj && obj->parsed)
                return obj;
 
-       if ((obj && obj->type == OBJ_BLOB && has_object_file(oid)) ||
-           (!obj && has_object_file(oid) &&
+       if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) ||
+           (!obj && repo_has_object_file(r, oid) &&
             oid_object_info(r, oid, NULL) == OBJ_BLOB)) {
                if (check_object_signature(repl, NULL, 0, NULL) < 0) {
                        error(_("sha1 mismatch %s"), oid_to_hex(oid));
@@ -270,7 +271,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
                return lookup_object(r, oid->hash);
        }
 
-       buffer = read_object_file(oid, &type, &size);
+       buffer = repo_read_object_file(r, oid, &type, &size);
        if (buffer) {
                if (check_object_signature(repl, buffer, size, type_name(type)) < 0) {
                        free(buffer);
@@ -482,27 +483,26 @@ struct raw_object_store *raw_object_store_new(void)
        return o;
 }
 
-static void free_alt_odb(struct alternate_object_database *alt)
+static void free_object_directory(struct object_directory *odb)
 {
-       strbuf_release(&alt->scratch);
-       oid_array_clear(&alt->loose_objects_cache);
-       free(alt);
+       free(odb->path);
+       odb_clear_loose_cache(odb);
+       free(odb);
 }
 
-static void free_alt_odbs(struct raw_object_store *o)
+static void free_object_directories(struct raw_object_store *o)
 {
-       while (o->alt_odb_list) {
-               struct alternate_object_database *next;
+       while (o->odb) {
+               struct object_directory *next;
 
-               next = o->alt_odb_list->next;
-               free_alt_odb(o->alt_odb_list);
-               o->alt_odb_list = next;
+               next = o->odb->next;
+               free_object_directory(o->odb);
+               o->odb = next;
        }
 }
 
 void raw_object_store_clear(struct raw_object_store *o)
 {
-       FREE_AND_NULL(o->objectdir);
        FREE_AND_NULL(o->alternate_db);
 
        oidmap_free(o->replace_map, 1);
@@ -512,8 +512,9 @@ void raw_object_store_clear(struct raw_object_store *o)
        o->commit_graph = NULL;
        o->commit_graph_attempted = 0;
 
-       free_alt_odbs(o);
-       o->alt_odb_tail = NULL;
+       free_object_directories(o);
+       o->odb_tail = NULL;
+       o->loaded_alternates = 0;
 
        INIT_LIST_HEAD(&o->packed_git_mru);
        close_all_packs(o);
@@ -540,7 +541,7 @@ void parsed_object_pool_clear(struct parsed_object_pool *o)
                if (obj->type == OBJ_TREE)
                        free_tree_buffer((struct tree*)obj);
                else if (obj->type == OBJ_COMMIT)
-                       release_commit_memory((struct commit*)obj);
+                       release_commit_memory(o, (struct commit*)obj);
                else if (obj->type == OBJ_TAG)
                        release_tag_memory((struct tag*)obj);
        }
index 9d1b951697e9552b80a502fd587e43b615369635..5566e94abed9f0838d4c1d999ee8e90159000256 100644 (file)
@@ -77,7 +77,7 @@ void bitmap_writer_build_type_index(struct packing_data *to_pack,
                        break;
 
                default:
-                       real_type = oid_object_info(the_repository,
+                       real_type = oid_object_info(to_pack->repo,
                                                    &entry->idx.oid, NULL);
                        break;
                }
@@ -262,7 +262,7 @@ void bitmap_writer_build(struct packing_data *to_pack)
        if (writer.show_progress)
                writer.progress = start_progress("Building bitmaps", writer.selected_nr);
 
-       repo_init_revisions(the_repository, &revs, NULL);
+       repo_init_revisions(to_pack->repo, &revs, NULL);
        revs.tag_objects = 1;
        revs.tree_objects = 1;
        revs.blob_objects = 1;
@@ -363,7 +363,7 @@ static int date_compare(const void *_a, const void *_b)
 void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack)
 {
        struct bitmap_index *bitmap_git;
-       if (!(bitmap_git = prepare_bitmap_git()))
+       if (!(bitmap_git = prepare_bitmap_git(to_pack->repo)))
                return;
 
        writer.reused = kh_init_sha1();
index 5848cc93aa254b8549f4c569882b5d781984ca47..4695aaf6b4eda41ec2479a326ddbe7a0bed065ab 100644 (file)
@@ -328,14 +328,15 @@ failed:
        return -1;
 }
 
-static int open_pack_bitmap(struct bitmap_index *bitmap_git)
+static int open_pack_bitmap(struct repository *r,
+                           struct bitmap_index *bitmap_git)
 {
        struct packed_git *p;
        int ret = -1;
 
        assert(!bitmap_git->map);
 
-       for (p = get_all_packs(the_repository); p; p = p->next) {
+       for (p = get_all_packs(r); p; p = p->next) {
                if (open_pack_bitmap_1(bitmap_git, p) == 0)
                        ret = 0;
        }
@@ -343,11 +344,11 @@ static int open_pack_bitmap(struct bitmap_index *bitmap_git)
        return ret;
 }
 
-struct bitmap_index *prepare_bitmap_git(void)
+struct bitmap_index *prepare_bitmap_git(struct repository *r)
 {
        struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
 
-       if (!open_pack_bitmap(bitmap_git) && !load_pack_bitmap(bitmap_git))
+       if (!open_pack_bitmap(r, bitmap_git) && !load_pack_bitmap(bitmap_git))
                return bitmap_git;
 
        free_bitmap_index(bitmap_git);
@@ -690,7 +691,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs)
        struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
        /* try to open a bitmapped pack, but don't parse it yet
         * because we may not need to use it */
-       if (open_pack_bitmap(bitmap_git) < 0)
+       if (open_pack_bitmap(revs->repo, bitmap_git) < 0)
                goto cleanup;
 
        for (i = 0; i < revs->pending.nr; ++i) {
@@ -955,7 +956,7 @@ void test_bitmap_walk(struct rev_info *revs)
        struct bitmap_test_data tdata;
        struct bitmap_index *bitmap_git;
 
-       if (!(bitmap_git = prepare_bitmap_git()))
+       if (!(bitmap_git = prepare_bitmap_git(revs->repo)))
                die("failed to load bitmap indexes");
 
        if (revs->pending.nr != 1)
index 189dd68ad30b14281b42ea43bb21739006ea6061..8418ba8c7929ed49aec17d5a25d8d0a309f971b8 100644 (file)
@@ -6,6 +6,7 @@
 #include "pack-objects.h"
 
 struct commit;
+struct repository;
 struct rev_info;
 
 struct bitmap_disk_header {
@@ -39,7 +40,7 @@ typedef int (*show_reachable_fn)(
 
 struct bitmap_index;
 
-struct bitmap_index *prepare_bitmap_git(void);
+struct bitmap_index *prepare_bitmap_git(struct repository *r);
 void count_bitmap_commit_list(struct bitmap_index *, uint32_t *commits,
                              uint32_t *trees, uint32_t *blobs, uint32_t *tags);
 void traverse_bitmap_commit_list(struct bitmap_index *,
index fa5f0ff8fa57461486ab2f82e58ad3eeffab77f1..2cc3603189b8cd27781fd16cba2eb10a16817038 100644 (file)
@@ -48,7 +48,8 @@ int check_pack_crc(struct packed_git *p, struct pack_window **w_curs,
        return data_crc != ntohl(*index_crc);
 }
 
-static int verify_packfile(struct packed_git *p,
+static int verify_packfile(struct repository *r,
+                          struct packed_git *p,
                           struct pack_window **w_curs,
                           verify_fn fn,
                           struct progress *progress, uint32_t base_count)
@@ -135,7 +136,7 @@ static int verify_packfile(struct packed_git *p,
                        data = NULL;
                        data_valid = 0;
                } else {
-                       data = unpack_entry(the_repository, p, entries[i].offset, &type, &size);
+                       data = unpack_entry(r, p, entries[i].offset, &type, &size);
                        data_valid = 1;
                }
 
@@ -186,7 +187,7 @@ int verify_pack_index(struct packed_git *p)
        return err;
 }
 
-int verify_pack(struct packed_git *p, verify_fn fn,
+int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,
                struct progress *progress, uint32_t base_count)
 {
        int err = 0;
@@ -196,7 +197,7 @@ int verify_pack(struct packed_git *p, verify_fn fn,
        if (!p->index_data)
                return -1;
 
-       err |= verify_packfile(p, &w_curs, fn, progress, base_count);
+       err |= verify_packfile(r, p, &w_curs, fn, progress, base_count);
        unuse_pack(&w_curs);
 
        return err;
index b6cdbb0166fb5a3d6af3cfda2e7e259e3d54abe8..e7cd337bee53cb02282a14c8aa88f2342edd2eb2 100644 (file)
@@ -99,7 +99,7 @@ static void prepare_in_pack_by_idx(struct packing_data *pdata)
         * (i.e. in_pack_idx also zero) should return NULL.
         */
        mapping[cnt++] = NULL;
-       for (p = get_all_packs(the_repository); p; p = p->next, cnt++) {
+       for (p = get_all_packs(pdata->repo); p; p = p->next, cnt++) {
                if (cnt == nr) {
                        free(mapping);
                        return;
@@ -133,8 +133,10 @@ void oe_map_new_pack(struct packing_data *pack,
 }
 
 /* assume pdata is already zero'd by caller */
-void prepare_packing_data(struct packing_data *pdata)
+void prepare_packing_data(struct repository *r, struct packing_data *pdata)
 {
+       pdata->repo = r;
+
        if (git_env_bool("GIT_TEST_FULL_IN_PACK_ARRAY", 0)) {
                /*
                 * do not initialize in_pack_by_idx[] to force the
@@ -148,9 +150,7 @@ void prepare_packing_data(struct packing_data *pdata)
                                             1U << OE_SIZE_BITS);
        pdata->oe_delta_size_limit = git_env_ulong("GIT_TEST_OE_DELTA_SIZE",
                                                   1UL << OE_DELTA_SIZE_BITS);
-#ifndef NO_PTHREADS
-       pthread_mutex_init(&pdata->lock, NULL);
-#endif
+       init_recursive_mutex(&pdata->odb_lock);
 }
 
 struct object_entry *packlist_alloc(struct packing_data *pdata,
index dc869f26c2b753593a875f275d896933b7723b31..6bfacc7d2cedd7f399d7807b7746b6ed66737eed 100644 (file)
@@ -5,6 +5,8 @@
 #include "thread-utils.h"
 #include "pack.h"
 
+struct repository;
+
 #define DEFAULT_DELTA_CACHE_SIZE (256 * 1024 * 1024)
 
 #define OE_DFS_STATE_BITS      2
@@ -127,6 +129,7 @@ struct object_entry {
 };
 
 struct packing_data {
+       struct repository *repo;
        struct object_entry *objects;
        uint32_t nr_objects, nr_alloc;
 
@@ -145,7 +148,11 @@ struct packing_data {
        struct packed_git **in_pack_by_idx;
        struct packed_git **in_pack;
 
-       pthread_mutex_t lock;
+       /*
+        * During packing with multiple threads, protect the in-core
+        * object database from concurrent accesses.
+        */
+       pthread_mutex_t odb_lock;
 
        /*
         * This list contains entries for bases which we know the other side
@@ -163,15 +170,16 @@ struct packing_data {
        unsigned char *layer;
 };
 
-void prepare_packing_data(struct packing_data *pdata);
+void prepare_packing_data(struct repository *r, struct packing_data *pdata);
 
+/* Protect access to object database */
 static inline void packing_data_lock(struct packing_data *pdata)
 {
-       pthread_mutex_lock(&pdata->lock);
+       pthread_mutex_lock(&pdata->odb_lock);
 }
 static inline void packing_data_unlock(struct packing_data *pdata)
 {
-       pthread_mutex_unlock(&pdata->lock);
+       pthread_mutex_unlock(&pdata->odb_lock);
 }
 
 struct object_entry *packlist_alloc(struct packing_data *pdata,
diff --git a/pack.h b/pack.h
index 34a9d458b411927b7c1e121e88387e022841ea2a..da99fdd1d2baf7574b606f4c2bc6e1dcfeacf0fa 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -4,6 +4,8 @@
 #include "object.h"
 #include "csum-file.h"
 
+struct repository;
+
 /*
  * Packed object header
  */
@@ -80,7 +82,7 @@ typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned lo
 extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1);
 extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
 extern int verify_pack_index(struct packed_git *);
-extern int verify_pack(struct packed_git *, verify_fn fn, struct progress *, uint32_t);
+extern int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);
 extern off_t write_pack_header(struct hashfile *f, uint32_t);
 extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
 extern char *index_pack_lockfile(int fd);
index d1e6683ffe877d9bf1b0996f25f0720fdffe983a..16bcb75262d918fc7b88cc5141213e951be145cc 100644 (file)
@@ -971,16 +971,16 @@ static void prepare_packed_git_mru(struct repository *r)
 
 static void prepare_packed_git(struct repository *r)
 {
-       struct alternate_object_database *alt;
+       struct object_directory *odb;
 
        if (r->objects->packed_git_initialized)
                return;
-       prepare_multi_pack_index_one(r, r->objects->objectdir, 1);
-       prepare_packed_git_one(r, r->objects->objectdir, 1);
+
        prepare_alt_odb(r);
-       for (alt = r->objects->alt_odb_list; alt; alt = alt->next) {
-               prepare_multi_pack_index_one(r, alt->path, 0);
-               prepare_packed_git_one(r, alt->path, 0);
+       for (odb = r->objects->odb; odb; odb = odb->next) {
+               int local = (odb == r->objects->odb);
+               prepare_multi_pack_index_one(r, odb->path, local);
+               prepare_packed_git_one(r, odb->path, local);
        }
        rearrange_packed_git(r);
 
@@ -992,6 +992,11 @@ static void prepare_packed_git(struct repository *r)
 
 void reprepare_packed_git(struct repository *r)
 {
+       struct object_directory *odb;
+
+       for (odb = r->objects->odb; odb; odb = odb->next)
+               odb_clear_loose_cache(odb);
+
        r->objects->approximate_object_count_valid = 0;
        r->objects->packed_git_initialized = 0;
        prepare_packed_git(r);
@@ -1143,12 +1148,13 @@ void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1)
        p->num_bad_objects++;
 }
 
-const struct packed_git *has_packed_and_bad(const unsigned char *sha1)
+const struct packed_git *has_packed_and_bad(struct repository *r,
+                                           const unsigned char *sha1)
 {
        struct packed_git *p;
        unsigned i;
 
-       for (p = the_repository->objects->packed_git; p; p = p->next)
+       for (p = r->objects->packed_git; p; p = p->next)
                for (i = 0; i < p->num_bad_objects; i++)
                        if (hasheq(sha1,
                                   p->bad_object_sha1 + the_hash_algo->rawsz * i))
@@ -2092,7 +2098,7 @@ static int add_promisor_object(const struct object_id *oid,
                         */
                        return 0;
                while (tree_entry_gently(&desc, &entry))
-                       oidset_insert(set, entry.oid);
+                       oidset_insert(set, &entry.oid);
        } else if (obj->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *) obj;
                struct commit_list *parents = commit->parents;
index 6c4037605d0dfee59a084c440506f1af11708d63..d70c6d9afb94c77c285fe8ee3237f7a40867157a 100644 (file)
@@ -146,7 +146,7 @@ extern int packed_object_info(struct repository *r,
                              off_t offset, struct object_info *);
 
 extern void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1);
-extern const struct packed_git *has_packed_and_bad(const unsigned char *sha1);
+extern const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1);
 
 /*
  * Iff a pack file in the given repository contains the object named by sha1,
index 8c9edce52f63bcb1085b119b3a2264a97b1fb374..e2f3eaed072f77d63890ec814d810199f57248d5 100644 (file)
@@ -18,7 +18,8 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
        } else {
                v = strtol(arg, (char **)&arg, 10);
                if (*arg)
-                       return opterror(opt, "expects a numerical value", 0);
+                       return error(_("option `%s' expects a numerical value"),
+                                    opt->long_name);
                if (v && v < MINIMUM_ABBREV)
                        v = MINIMUM_ABBREV;
                else if (v > 40)
@@ -47,8 +48,8 @@ int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
                arg = unset ? "never" : (const char *)opt->defval;
        value = git_config_colorbool(NULL, arg);
        if (value < 0)
-               return opterror(opt,
-                       "expects \"always\", \"auto\", or \"never\"", 0);
+               return error(_("option `%s' expects \"always\", \"auto\", or \"never\""),
+                            opt->long_name);
        *(int *)opt->value = value;
        return 0;
 }
index 6932eaff6144bdf9fc8f430f149a73ade00f0d8b..9f84bacce64e72d117be2bdbe91db6d4c190e257 100644 (file)
@@ -32,7 +32,7 @@ static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
                p->argc--;
                *arg = *++p->argv;
        } else
-               return opterror(opt, "requires a value", flags);
+               return error(_("%s requires a value"), optname(opt, flags));
        return 0;
 }
 
@@ -49,7 +49,6 @@ static int opt_command_mode_error(const struct option *opt,
                                  int flags)
 {
        const struct option *that;
-       struct strbuf message = STRBUF_INIT;
        struct strbuf that_name = STRBUF_INIT;
 
        /*
@@ -67,13 +66,13 @@ static int opt_command_mode_error(const struct option *opt,
                        strbuf_addf(&that_name, "--%s", that->long_name);
                else
                        strbuf_addf(&that_name, "-%c", that->short_name);
-               strbuf_addf(&message, ": incompatible with %s", that_name.buf);
+               error(_("%s is incompatible with %s"),
+                     optname(opt, flags), that_name.buf);
                strbuf_release(&that_name);
-               opterror(opt, message.buf, flags);
-               strbuf_release(&message);
                return -1;
        }
-       return opterror(opt, ": incompatible with something else", flags);
+       return error(_("%s : incompatible with something else"),
+                    optname(opt, flags));
 }
 
 static int get_value(struct parse_opt_ctx_t *p,
@@ -86,11 +85,11 @@ static int get_value(struct parse_opt_ctx_t *p,
        int err;
 
        if (unset && p->opt)
-               return opterror(opt, "takes no value", flags);
+               return error(_("%s takes no value"), optname(opt, flags));
        if (unset && (opt->flags & PARSE_OPT_NONEG))
-               return opterror(opt, "isn't available", flags);
+               return error(_("%s isn't available"), optname(opt, flags));
        if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
-               return opterror(opt, "takes no value", flags);
+               return error(_("%s takes no value"), optname(opt, flags));
 
        switch (opt->type) {
        case OPTION_LOWLEVEL_CALLBACK:
@@ -176,7 +175,8 @@ static int get_value(struct parse_opt_ctx_t *p,
                        return -1;
                *(int *)opt->value = strtol(arg, (char **)&s, 10);
                if (*s)
-                       return opterror(opt, "expects a numerical value", flags);
+                       return error(_("%s expects a numerical value"),
+                                    optname(opt, flags));
                return 0;
 
        case OPTION_MAGNITUDE:
@@ -191,13 +191,13 @@ static int get_value(struct parse_opt_ctx_t *p,
                if (get_arg(p, opt, flags, &arg))
                        return -1;
                if (!git_parse_ulong(arg, opt->value))
-                       return opterror(opt,
-                               "expects a non-negative integer value with an optional k/m/g suffix",
-                               flags);
+                       return error(_("%s expects a non-negative integer value"
+                                      " with an optional k/m/g suffix"),
+                                    optname(opt, flags));
                return 0;
 
        default:
-               die("should not happen, someone must be hit on the forehead");
+               BUG("opt->type %d should not happen", opt->type);
        }
 }
 
@@ -236,7 +236,7 @@ static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *optio
 }
 
 static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
-                          const struct option *options)
+                         const struct option *options)
 {
        const struct option *all_opts = options;
        const char *arg_end = strchrnul(arg, '=');
@@ -257,7 +257,8 @@ again:
                        if (!rest)
                                continue;
                        if (*rest == '=')
-                               return opterror(options, "takes no value", flags);
+                               return error(_("%s takes no value"),
+                                            optname(options, flags));
                        if (*rest)
                                continue;
                        p->out[p->cpidx++] = arg - 2;
@@ -318,8 +319,8 @@ is_abbreviated:
        }
 
        if (ambiguous_option) {
-               error("Ambiguous option: %s "
-                       "(could be --%s%s or --%s%s)",
+               error(_("ambiguous option: %s "
+                       "(could be --%s%s or --%s%s)"),
                        arg,
                        (ambiguous_flags & OPT_UNSET) ?  "no-" : "",
                        ambiguous_option->long_name,
@@ -352,7 +353,7 @@ static void check_typos(const char *arg, const struct option *options)
                return;
 
        if (starts_with(arg, "no-")) {
-               error ("did you mean `--%s` (with two dashes ?)", arg);
+               error(_("did you mean `--%s` (with two dashes ?)"), arg);
                exit(129);
        }
 
@@ -360,7 +361,7 @@ static void check_typos(const char *arg, const struct option *options)
                if (!options->long_name)
                        continue;
                if (starts_with(options->long_name, arg)) {
-                       error ("did you mean `--%s` (with two dashes ?)", arg);
+                       error(_("did you mean `--%s` (with two dashes ?)"), arg);
                        exit(129);
                }
        }
@@ -423,7 +424,7 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
        ctx->flags = flags;
        if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
            (flags & PARSE_OPT_STOP_AT_NON_OPTION))
-               die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
+               BUG("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
        parse_options_check(options);
 }
 
@@ -645,11 +646,11 @@ int parse_options(int argc, const char **argv, const char *prefix,
                break;
        default: /* PARSE_OPT_UNKNOWN */
                if (ctx.argv[0][1] == '-') {
-                       error("unknown option `%s'", ctx.argv[0] + 2);
+                       error(_("unknown option `%s'"), ctx.argv[0] + 2);
                } else if (isascii(*ctx.opt)) {
-                       error("unknown switch `%c'", *ctx.opt);
+                       error(_("unknown switch `%c'"), *ctx.opt);
                } else {
-                       error("unknown non-ascii option in string: `%s'",
+                       error(_("unknown non-ascii option in string: `%s'"),
                              ctx.argv[0]);
                }
                usage_with_options(usagestr, options);
@@ -775,12 +776,17 @@ void NORETURN usage_msg_opt(const char *msg,
        usage_with_options(usagestr, options);
 }
 
-#undef opterror
-int opterror(const struct option *opt, const char *reason, int flags)
+const char *optname(const struct option *opt, int flags)
 {
+       static struct strbuf sb = STRBUF_INIT;
+
+       strbuf_reset(&sb);
        if (flags & OPT_SHORT)
-               return error("switch `%c' %s", opt->short_name, reason);
-       if (flags & OPT_UNSET)
-               return error("option `no-%s' %s", opt->long_name, reason);
-       return error("option `%s' %s", opt->long_name, reason);
+               strbuf_addf(&sb, "switch `%c'", opt->short_name);
+       else if (flags & OPT_UNSET)
+               strbuf_addf(&sb, "option `no-%s'", opt->long_name);
+       else
+               strbuf_addf(&sb, "option `%s'", opt->long_name);
+
+       return sb.buf;
 }
index a650a7d220bd972b10b7e4a30119e9de3b8ba23a..14fe32428e57aee0716517219e3fc925ac5e8a0d 100644 (file)
@@ -175,21 +175,18 @@ struct option {
  * Returns the number of arguments left in argv[].
  */
 extern int parse_options(int argc, const char **argv, const char *prefix,
-                         const struct option *options,
-                         const char * const usagestr[], int flags);
+                        const struct option *options,
+                        const char * const usagestr[], int flags);
 
 extern NORETURN void usage_with_options(const char * const *usagestr,
-                                        const struct option *options);
+                                       const struct option *options);
 
 extern NORETURN void usage_msg_opt(const char *msg,
                                   const char * const *usagestr,
                                   const struct option *options);
 
 extern int optbug(const struct option *opt, const char *reason);
-extern int opterror(const struct option *opt, const char *reason, int flags);
-#if defined(__GNUC__)
-#define opterror(o,r,f) (opterror((o),(r),(f)), const_error())
-#endif
+const char *optname(const struct option *opt, int flags);
 
 /*
  * Use these assertions for callbacks that expect to be called with NONEG and
diff --git a/path.c b/path.c
index dc3294c71e1e72ae2aae9164c790e27d1237e956..03ab712839a7878e3d3b5d79c698f4e2bb29c8b1 100644 (file)
--- a/path.c
+++ b/path.c
@@ -385,7 +385,7 @@ static void adjust_git_path(const struct repository *repo,
                strbuf_splice(buf, 0, buf->len,
                              repo->index_file, strlen(repo->index_file));
        else if (dir_prefix(base, "objects"))
-               replace_dir(buf, git_dir_len + 7, repo->objects->objectdir);
+               replace_dir(buf, git_dir_len + 7, repo->objects->odb->path);
        else if (git_hooks_path && dir_prefix(base, "hooks"))
                replace_dir(buf, git_dir_len + 5, git_hooks_path);
        else if (repo->different_commondir)
diff --git a/path.h b/path.h
index b654ea8ff5f2e701239ec17b8d1fc81cebfc25dd..651e6157fc480264e3495bd508a88d0835de1379 100644 (file)
--- a/path.h
+++ b/path.h
@@ -165,7 +165,7 @@ extern void report_linked_checkout_garbage(void);
        const char *git_path_##var(struct repository *r) \
        { \
                if (!r->cached_paths.var) \
-                       r->cached_paths.var = git_pathdup(filename); \
+                       r->cached_paths.var = repo_git_path(r, filename); \
                return r->cached_paths.var; \
        }
 
index 6f005996fdc8f3e412b0faafc12e65aed359be3b..e85298f68cfeee14494466b0604e572a5c345810 100644 (file)
@@ -659,3 +659,41 @@ void clear_pathspec(struct pathspec *pathspec)
        FREE_AND_NULL(pathspec->items);
        pathspec->nr = 0;
 }
+
+int match_pathspec_attrs(const struct index_state *istate,
+                        const char *name, int namelen,
+                        const struct pathspec_item *item)
+{
+       int i;
+       char *to_free = NULL;
+
+       if (name[namelen])
+               name = to_free = xmemdupz(name, namelen);
+
+       git_check_attr(istate, name, item->attr_check);
+
+       free(to_free);
+
+       for (i = 0; i < item->attr_match_nr; i++) {
+               const char *value;
+               int matched;
+               enum attr_match_mode match_mode;
+
+               value = item->attr_check->items[i].value;
+               match_mode = item->attr_match[i].match_mode;
+
+               if (ATTR_TRUE(value))
+                       matched = (match_mode == MATCH_SET);
+               else if (ATTR_FALSE(value))
+                       matched = (match_mode == MATCH_UNSET);
+               else if (ATTR_UNSET(value))
+                       matched = (match_mode == MATCH_UNSPECIFIED);
+               else
+                       matched = (match_mode == MATCH_VALUE &&
+                                  !strcmp(item->attr_match[i].value, value));
+               if (!matched)
+                       return 0;
+       }
+
+       return 1;
+}
index a6525a65517bd08921ad836880184048e93c4cfa..1c18a2c90c4148471f52e27c39685e80a9a414e1 100644 (file)
@@ -80,13 +80,13 @@ struct pathspec {
  * Any arguments used are copied. It is safe for the caller to modify
  * or free 'prefix' and 'args' after calling this function.
  */
-extern void parse_pathspec(struct pathspec *pathspec,
-                          unsigned magic_mask,
-                          unsigned flags,
-                          const char *prefix,
-                          const char **args);
-extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
-extern void clear_pathspec(struct pathspec *);
+void parse_pathspec(struct pathspec *pathspec,
+                   unsigned magic_mask,
+                   unsigned flags,
+                   const char *prefix,
+                   const char **args);
+void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
+void clear_pathspec(struct pathspec *);
 
 static inline int ps_strncmp(const struct pathspec_item *item,
                             const char *s1, const char *s2, size_t n)
@@ -106,10 +106,13 @@ static inline int ps_strcmp(const struct pathspec_item *item,
                return strcmp(s1, s2);
 }
 
-extern void add_pathspec_matches_against_index(const struct pathspec *pathspec,
-                                              const struct index_state *istate,
-                                              char *seen);
-extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec,
-                                                  const struct index_state *istate);
+void add_pathspec_matches_against_index(const struct pathspec *pathspec,
+                                       const struct index_state *istate,
+                                       char *seen);
+char *find_pathspecs_matching_against_index(const struct pathspec *pathspec,
+                                           const struct index_state *istate);
+int match_pathspec_attrs(const struct index_state *istate,
+                        const char *name, int namelen,
+                        const struct pathspec_item *item);
 
 #endif /* PATHSPEC_H */
index 04d10bbd037b393574f8453cbd00f94e0b2e7e99..d4b71d3e82b0e54ef4105748fdfdc15b438badfb 100644 (file)
@@ -129,12 +129,14 @@ static void set_packet_header(char *buf, const int size)
        #undef hex
 }
 
-static void format_packet(struct strbuf *out, const char *fmt, va_list args)
+static void format_packet(struct strbuf *out, const char *prefix,
+                         const char *fmt, va_list args)
 {
        size_t orig_len, n;
 
        orig_len = out->len;
        strbuf_addstr(out, "0000");
+       strbuf_addstr(out, prefix);
        strbuf_vaddf(out, fmt, args);
        n = out->len - orig_len;
 
@@ -145,13 +147,13 @@ static void format_packet(struct strbuf *out, const char *fmt, va_list args)
        packet_trace(out->buf + orig_len + 4, n - 4, 1);
 }
 
-static int packet_write_fmt_1(int fd, int gently,
+static int packet_write_fmt_1(int fd, int gently, const char *prefix,
                              const char *fmt, va_list args)
 {
        static struct strbuf buf = STRBUF_INIT;
 
        strbuf_reset(&buf);
-       format_packet(&buf, fmt, args);
+       format_packet(&buf, prefix, fmt, args);
        if (write_in_full(fd, buf.buf, buf.len) < 0) {
                if (!gently) {
                        check_pipe(errno);
@@ -168,7 +170,7 @@ void packet_write_fmt(int fd, const char *fmt, ...)
        va_list args;
 
        va_start(args, fmt);
-       packet_write_fmt_1(fd, 0, fmt, args);
+       packet_write_fmt_1(fd, 0, "", fmt, args);
        va_end(args);
 }
 
@@ -178,7 +180,7 @@ int packet_write_fmt_gently(int fd, const char *fmt, ...)
        va_list args;
 
        va_start(args, fmt);
-       status = packet_write_fmt_1(fd, 1, fmt, args);
+       status = packet_write_fmt_1(fd, 1, "", fmt, args);
        va_end(args);
        return status;
 }
@@ -211,7 +213,7 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
        va_list args;
 
        va_start(args, fmt);
-       format_packet(buf, fmt, args);
+       format_packet(buf, "", fmt, args);
        va_end(args);
 }
 
@@ -346,6 +348,10 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
                return PACKET_READ_EOF;
        }
 
+       if ((options & PACKET_READ_DIE_ON_ERR_PACKET) &&
+           starts_with(buffer, "ERR "))
+               die(_("remote error: %s"), buffer + 4);
+
        if ((options & PACKET_READ_CHOMP_NEWLINE) &&
            len && buffer[len-1] == '\n')
                len--;
@@ -433,6 +439,29 @@ ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out)
        return sb_out->len - orig_len;
 }
 
+int recv_sideband(const char *me, int in_stream, int out)
+{
+       char buf[LARGE_PACKET_MAX + 1];
+       int len;
+       struct strbuf scratch = STRBUF_INIT;
+       enum sideband_type sideband_type;
+
+       while (1) {
+               len = packet_read(in_stream, NULL, NULL, buf, LARGE_PACKET_MAX,
+                                 0);
+               if (!demultiplex_sideband(me, buf, len, 0, &scratch,
+                                         &sideband_type))
+                       continue;
+               switch (sideband_type) {
+               case SIDEBAND_PRIMARY:
+                       write_or_die(out, buf + 1, len - 1);
+                       break;
+               default: /* errors: message already written */
+                       return sideband_type;
+               }
+       }
+}
+
 /* Packet Reader Functions */
 void packet_reader_init(struct packet_reader *reader, int fd,
                        char *src_buffer, size_t src_len,
@@ -446,25 +475,43 @@ void packet_reader_init(struct packet_reader *reader, int fd,
        reader->buffer = packet_buffer;
        reader->buffer_size = sizeof(packet_buffer);
        reader->options = options;
+       reader->me = "git";
 }
 
 enum packet_read_status packet_reader_read(struct packet_reader *reader)
 {
+       struct strbuf scratch = STRBUF_INIT;
+
        if (reader->line_peeked) {
                reader->line_peeked = 0;
                return reader->status;
        }
 
-       reader->status = packet_read_with_status(reader->fd,
-                                                &reader->src_buffer,
-                                                &reader->src_len,
-                                                reader->buffer,
-                                                reader->buffer_size,
-                                                &reader->pktlen,
-                                                reader->options);
+       /*
+        * Consume all progress packets until a primary payload packet is
+        * received
+        */
+       while (1) {
+               enum sideband_type sideband_type;
+               reader->status = packet_read_with_status(reader->fd,
+                                                        &reader->src_buffer,
+                                                        &reader->src_len,
+                                                        reader->buffer,
+                                                        reader->buffer_size,
+                                                        &reader->pktlen,
+                                                        reader->options);
+               if (!reader->use_sideband)
+                       break;
+               if (demultiplex_sideband(reader->me, reader->buffer,
+                                        reader->pktlen, 1, &scratch,
+                                        &sideband_type))
+                       break;
+       }
 
        if (reader->status == PACKET_READ_NORMAL)
-               reader->line = reader->buffer;
+               /* Skip the sideband designator if sideband is used */
+               reader->line = reader->use_sideband ?
+                       reader->buffer + 1 : reader->buffer;
        else
                reader->line = NULL;
 
@@ -482,3 +529,39 @@ enum packet_read_status packet_reader_peek(struct packet_reader *reader)
        reader->line_peeked = 1;
        return reader->status;
 }
+
+void packet_writer_init(struct packet_writer *writer, int dest_fd)
+{
+       writer->dest_fd = dest_fd;
+       writer->use_sideband = 0;
+}
+
+void packet_writer_write(struct packet_writer *writer, const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       packet_write_fmt_1(writer->dest_fd, 0,
+                          writer->use_sideband ? "\001" : "", fmt, args);
+       va_end(args);
+}
+
+void packet_writer_error(struct packet_writer *writer, const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       packet_write_fmt_1(writer->dest_fd, 0,
+                          writer->use_sideband ? "\003" : "ERR ", fmt, args);
+       va_end(args);
+}
+
+void packet_writer_delim(struct packet_writer *writer)
+{
+       packet_delim(writer->dest_fd);
+}
+
+void packet_writer_flush(struct packet_writer *writer)
+{
+       packet_flush(writer->dest_fd);
+}
index 5b28d43472db41a59f0a44845953f163748593b0..ad9a4a2cd7c332cb1c4d1f26268f1ef45f2d7639 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "git-compat-util.h"
 #include "strbuf.h"
+#include "sideband.h"
 
 /*
  * Write a packetized stream, where each line is preceded by
@@ -62,9 +63,13 @@ int write_packetized_from_buf(const char *src_in, size_t len, int fd_out);
  *
  * If options contains PACKET_READ_CHOMP_NEWLINE, a trailing newline (if
  * present) is removed from the buffer before returning.
+ *
+ * If options contains PACKET_READ_DIE_ON_ERR_PACKET, it dies when it sees an
+ * ERR packet.
  */
-#define PACKET_READ_GENTLE_ON_EOF (1u<<0)
-#define PACKET_READ_CHOMP_NEWLINE (1u<<1)
+#define PACKET_READ_GENTLE_ON_EOF     (1u<<0)
+#define PACKET_READ_CHOMP_NEWLINE     (1u<<1)
+#define PACKET_READ_DIE_ON_ERR_PACKET (1u<<2)
 int packet_read(int fd, char **src_buffer, size_t *src_len, char
                *buffer, unsigned size, int options);
 
@@ -116,6 +121,21 @@ char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);
  */
 ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out);
 
+/*
+ * Receive multiplexed output stream over git native protocol.
+ * in_stream is the input stream from the remote, which carries data
+ * in pkt_line format with band designator.  Demultiplex it into out
+ * and err and return error appropriately.  Band #1 carries the
+ * primary payload.  Things coming over band #2 is not necessarily
+ * error; they are usually informative message on the standard error
+ * stream, aka "verbose").  A message over band #3 is a signal that
+ * the remote died unexpectedly.  A flush() concludes the stream.
+ *
+ * Returns SIDEBAND_FLUSH upon a normal conclusion, and SIDEBAND_PROTOCOL_ERROR
+ * or SIDEBAND_REMOTE_ERROR if an error occurred.
+ */
+int recv_sideband(const char *me, int in_stream, int out);
+
 struct packet_reader {
        /* source file descriptor */
        int fd;
@@ -142,6 +162,9 @@ struct packet_reader {
 
        /* indicates if a line has been peeked */
        int line_peeked;
+
+       unsigned use_sideband : 1;
+       const char *me;
 };
 
 /*
@@ -179,4 +202,19 @@ extern enum packet_read_status packet_reader_peek(struct packet_reader *reader);
 #define LARGE_PACKET_DATA_MAX (LARGE_PACKET_MAX - 4)
 extern char packet_buffer[LARGE_PACKET_MAX];
 
+struct packet_writer {
+       int dest_fd;
+       unsigned use_sideband : 1;
+};
+
+void packet_writer_init(struct packet_writer *writer, int dest_fd);
+
+/* These functions die upon failure. */
+__attribute__((format (printf, 2, 3)))
+void packet_writer_write(struct packet_writer *writer, const char *fmt, ...);
+__attribute__((format (printf, 2, 3)))
+void packet_writer_error(struct packet_writer *writer, const char *fmt, ...);
+void packet_writer_delim(struct packet_writer *writer);
+void packet_writer_flush(struct packet_writer *writer);
+
 #endif
index b83a3ecd2331af7f2e7e03bd76336cde3c2a6dfc..0ab45d10d702373e212fb36e4fa66feb26669ee7 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -595,14 +595,15 @@ static char *replace_encoding_header(char *buf, const char *encoding)
        return strbuf_detach(&tmp, NULL);
 }
 
-const char *logmsg_reencode(const struct commit *commit,
-                           char **commit_encoding,
-                           const char *output_encoding)
+const char *repo_logmsg_reencode(struct repository *r,
+                                const struct commit *commit,
+                                char **commit_encoding,
+                                const char *output_encoding)
 {
        static const char *utf8 = "UTF-8";
        const char *use_encoding;
        char *encoding;
-       const char *msg = get_commit_buffer(commit, NULL);
+       const char *msg = repo_get_commit_buffer(r, commit, NULL);
        char *out;
 
        if (!output_encoding || !*output_encoding) {
@@ -630,7 +631,7 @@ const char *logmsg_reencode(const struct commit *commit,
                 * the cached copy from get_commit_buffer, we need to duplicate it
                 * to avoid munging the cached copy.
                 */
-               if (msg == get_cached_commit_buffer(the_repository, commit, NULL))
+               if (msg == get_cached_commit_buffer(r, commit, NULL))
                        out = xstrdup(msg);
                else
                        out = (char *)msg;
@@ -644,7 +645,7 @@ const char *logmsg_reencode(const struct commit *commit,
                 */
                out = reencode_string(msg, output_encoding, use_encoding);
                if (out)
-                       unuse_commit_buffer(commit, msg);
+                       repo_unuse_commit_buffer(r, commit, msg);
        }
 
        /*
@@ -1084,6 +1085,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
        struct commit_list *p;
        const char *arg;
        int ch;
+       char **slot;
 
        /* these are independent of the commit */
        switch (placeholder[0]) {
@@ -1194,6 +1196,14 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                load_ref_decorations(NULL, DECORATE_SHORT_REFS);
                format_decorations_extended(sb, commit, c->auto_color, "", ", ", "");
                return 1;
+       case 'S':               /* tag/branch like --source */
+               if (!(c->pretty_ctx->rev && c->pretty_ctx->rev->sources))
+                       return 0;
+               slot = revision_sources_at(c->pretty_ctx->rev->sources, commit);
+               if (!(slot && *slot))
+                       return 0;
+               strbuf_addstr(sb, *slot);
+               return 1;
        case 'g':               /* reflog info */
                switch(placeholder[1]) {
                case 'd':       /* reflog selector */
@@ -1498,6 +1508,9 @@ static size_t userformat_want_item(struct strbuf *sb, const char *placeholder,
        case 'N':
                w->notes = 1;
                break;
+       case 'S':
+               w->source = 1;
+               break;
        }
        return 0;
 }
@@ -1515,9 +1528,10 @@ void userformat_find_requirements(const char *fmt, struct userformat_want *w)
        strbuf_release(&dummy);
 }
 
-void format_commit_message(const struct commit *commit,
-                          const char *format, struct strbuf *sb,
-                          const struct pretty_print_context *pretty_ctx)
+void repo_format_commit_message(struct repository *r,
+                               const struct commit *commit,
+                               const char *format, struct strbuf *sb,
+                               const struct pretty_print_context *pretty_ctx)
 {
        struct format_commit_context context;
        const char *output_enc = pretty_ctx->output_encoding;
@@ -1531,9 +1545,9 @@ void format_commit_message(const struct commit *commit,
         * convert a commit message to UTF-8 first
         * as far as 'format_commit_item' assumes it in UTF-8
         */
-       context.message = logmsg_reencode(commit,
-                                         &context.commit_encoding,
-                                         utf8);
+       context.message = repo_logmsg_reencode(r, commit,
+                                              &context.commit_encoding,
+                                              utf8);
 
        strbuf_expand(sb, format, format_commit_item, &context);
        rewrap_message_tail(sb, &context, 0, 0, 0);
@@ -1557,7 +1571,7 @@ void format_commit_message(const struct commit *commit,
        }
 
        free(context.commit_encoding);
-       unuse_commit_buffer(commit, context.message);
+       repo_unuse_commit_buffer(r, commit, context.message);
 }
 
 static void pp_header(struct pretty_print_context *pp,
index 7359d318a92c167e0f36eabf73fdd1142f0e9031..4ad1fc31ff33408881abf2ea5e28ab813d6c1f85 100644 (file)
--- a/pretty.h
+++ b/pretty.h
@@ -60,6 +60,7 @@ static inline int cmit_fmt_is_mail(enum cmit_fmt fmt)
 
 struct userformat_want {
        unsigned notes:1;
+       unsigned source:1;
 };
 
 /* Set the flag "w->notes" if there is placeholder %N in "fmt". */
@@ -103,9 +104,14 @@ void pp_remainder(struct pretty_print_context *pp, const char **msg_p,
  * Put the result to "sb".
  * Please use this function for custom formats.
  */
-void format_commit_message(const struct commit *commit,
+void repo_format_commit_message(struct repository *r,
+                       const struct commit *commit,
                        const char *format, struct strbuf *sb,
                        const struct pretty_print_context *context);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define format_commit_message(c, f, s, con) \
+       repo_format_commit_message(the_repository, c, f, s, con)
+#endif
 
 /*
  * Parse given arguments from "arg", check it for correctness and
diff --git a/quote.c b/quote.c
index c95dd2cafbaa85c9c443a229134842bf06ce3200..7f2aa6faa43fed0cd19f23f6fcfdc7b0ebea5c01 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -234,7 +234,7 @@ static size_t next_quote_pos(const char *s, ssize_t maxlen)
  *     Return value is the same as in (1).
  */
 static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
-                                    struct strbuf *sb, FILE *fp, int no_dq)
+                                   struct strbuf *sb, FILE *fp, int no_dq)
 {
 #undef EMIT
 #define EMIT(c)                                 \
index bd45dc3e24d7dc28820d26cc6e6d377f92fda46d..9783c493a321046e80a36847d8c36d1af6d263e3 100644 (file)
@@ -316,7 +316,7 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
                        changed |= DATA_CHANGED;
                return changed;
        default:
-               die("internal error: ce_mode is %o", ce->ce_mode);
+               BUG("unsupported ce_mode: %o", ce->ce_mode);
        }
 
        changed |= match_stat_data(&ce->ce_stat_data, st);
@@ -672,7 +672,8 @@ static struct cache_entry *create_alias_ce(struct index_state *istate,
        struct cache_entry *new_entry;
 
        if (alias->ce_flags & CE_ADDED)
-               die("Will not add file alias '%s' ('%s' already exists in index)", ce->name, alias->name);
+               die(_("will not add file alias '%s' ('%s' already exists in index)"),
+                   ce->name, alias->name);
 
        /* Ok, create the new entry using the name of the existing alias */
        len = ce_namelen(alias);
@@ -687,7 +688,7 @@ void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
 {
        struct object_id oid;
        if (write_object_file("", 0, blob_type, &oid))
-               die("cannot create an empty blob in the object database");
+               die(_("cannot create an empty blob in the object database"));
        oidcpy(&ce->oid, &oid);
 }
 
@@ -702,13 +703,13 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        int intent_only = flags & ADD_CACHE_INTENT;
        int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
                          (intent_only ? ADD_CACHE_NEW_ONLY : 0));
-       int newflags = HASH_WRITE_OBJECT;
+       int hash_flags = HASH_WRITE_OBJECT;
 
-       if (flags & HASH_RENORMALIZE)
-               newflags |= HASH_RENORMALIZE;
+       if (flags & ADD_CACHE_RENORMALIZE)
+               hash_flags |= HASH_RENORMALIZE;
 
        if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
-               return error("%s: can only add regular files, symbolic links or git-directories", path);
+               return error(_("%s: can only add regular files, symbolic links or git-directories"), path);
 
        namelen = strlen(path);
        if (S_ISDIR(st_mode)) {
@@ -761,9 +762,9 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                }
        }
        if (!intent_only) {
-               if (index_path(istate, &ce->oid, path, st, newflags)) {
+               if (index_path(istate, &ce->oid, path, st, hash_flags)) {
                        discard_cache_entry(ce);
-                       return error("unable to index file %s", path);
+                       return error(_("unable to index file '%s'"), path);
                }
        } else
                set_object_name_for_intent_to_add_entry(ce);
@@ -782,7 +783,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                discard_cache_entry(ce);
        else if (add_index_entry(istate, ce, add_option)) {
                discard_cache_entry(ce);
-               return error("unable to add %s to index", path);
+               return error(_("unable to add '%s' to index"), path);
        }
        if (verbose && !was_same)
                printf("add '%s'\n", path);
@@ -793,7 +794,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int flags)
 {
        struct stat st;
        if (lstat(path, &st))
-               die_errno("unable to stat '%s'", path);
+               die_errno(_("unable to stat '%s'"), path);
        return add_to_index(istate, path, &st, flags);
 }
 
@@ -818,7 +819,7 @@ struct cache_entry *make_cache_entry(struct index_state *istate,
        int len;
 
        if (!verify_path(path, mode)) {
-               error("Invalid path '%s'", path);
+               error(_("invalid path '%s'"), path);
                return NULL;
        }
 
@@ -844,7 +845,7 @@ struct cache_entry *make_transient_cache_entry(unsigned int mode, const struct o
        int len;
 
        if (!verify_path(path, mode)) {
-               error("Invalid path '%s'", path);
+               error(_("invalid path '%s'"), path);
                return NULL;
        }
 
@@ -1297,12 +1298,12 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
        if (!ok_to_add)
                return -1;
        if (!verify_path(ce->name, ce->ce_mode))
-               return error("Invalid path '%s'", ce->name);
+               return error(_("invalid path '%s'"), ce->name);
 
        if (!skip_df_check &&
            check_file_directory_conflict(istate, ce, pos, ok_to_replace)) {
                if (!ok_to_replace)
-                       return error("'%s' appears as both a file and as a directory",
+                       return error(_("'%s' appears as both a file and as a directory"),
                                     ce->name);
                pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
                pos = -pos-1;
@@ -1491,11 +1492,11 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                                                  istate->cache_nr);
 
        trace_performance_enter();
-       modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
-       deleted_fmt = (in_porcelain ? "D\t%s\n" : "%s: needs update\n");
-       typechange_fmt = (in_porcelain ? "T\t%s\n" : "%s needs update\n");
-       added_fmt = (in_porcelain ? "A\t%s\n" : "%s needs update\n");
-       unmerged_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
+       modified_fmt   = in_porcelain ? "M\t%s\n" : "%s: needs update\n";
+       deleted_fmt    = in_porcelain ? "D\t%s\n" : "%s: needs update\n";
+       typechange_fmt = in_porcelain ? "T\t%s\n" : "%s: needs update\n";
+       added_fmt      = in_porcelain ? "A\t%s\n" : "%s: needs update\n";
+       unmerged_fmt   = in_porcelain ? "U\t%s\n" : "%s: needs merge\n";
        /*
         * Use the multi-threaded preload_index() to refresh most of the
         * cache entries quickly then in the single threaded loop below,
@@ -1682,10 +1683,10 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
        int hdr_version;
 
        if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
-               return error("bad signature");
+               return error(_("bad signature 0x%08x"), hdr->hdr_signature);
        hdr_version = ntohl(hdr->hdr_version);
        if (hdr_version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < hdr_version)
-               return error("bad index version %d", hdr_version);
+               return error(_("bad index version %d"), hdr_version);
 
        if (!verify_index_checksum)
                return 0;
@@ -1694,7 +1695,7 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
        the_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz);
        the_hash_algo->final_fn(hash, &c);
        if (!hasheq(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz))
-               return error("bad index file sha1 signature");
+               return error(_("bad index file sha1 signature"));
        return 0;
 }
 
@@ -1724,9 +1725,9 @@ static int read_index_extension(struct index_state *istate,
                break;
        default:
                if (*ext < 'A' || 'Z' < *ext)
-                       return error("index uses %.4s extension, which we do not understand",
+                       return error(_("index uses %.4s extension, which we do not understand"),
                                     ext);
-               fprintf(stderr, "ignoring %.4s extension\n", ext);
+               fprintf_ln(stderr, _("ignoring %.4s extension"), ext);
                break;
        }
        return 0;
@@ -1773,7 +1774,7 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
                extended_flags = get_be16(&ondisk2->flags2) << 16;
                /* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
                if (extended_flags & ~CE_EXTENDED_FLAGS)
-                       die("Unknown index entry format %08x", extended_flags);
+                       die(_("unknown index entry format 0x%08x"), extended_flags);
                flags |= extended_flags;
                name = ondisk2->name;
        }
@@ -1844,13 +1845,13 @@ static void check_ce_order(struct index_state *istate)
                int name_compare = strcmp(ce->name, next_ce->name);
 
                if (0 < name_compare)
-                       die("unordered stage entries in index");
+                       die(_("unordered stage entries in index"));
                if (!name_compare) {
                        if (!ce_stage(ce))
-                               die("multiple stage entries for merged file '%s'",
+                               die(_("multiple stage entries for merged file '%s'"),
                                    ce->name);
                        if (ce_stage(ce) > ce_stage(next_ce))
-                               die("unordered stage entries for '%s'",
+                               die(_("unordered stage entries for '%s'"),
                                    ce->name);
                }
        }
@@ -2144,19 +2145,19 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
        if (fd < 0) {
                if (!must_exist && errno == ENOENT)
                        return 0;
-               die_errno("%s: index file open failed", path);
+               die_errno(_("%s: index file open failed"), path);
        }
 
        if (fstat(fd, &st))
-               die_errno("cannot stat the open index");
+               die_errno(_("%s: cannot stat the open index"), path);
 
        mmap_size = xsize_t(st.st_size);
        if (mmap_size < sizeof(struct cache_header) + the_hash_algo->rawsz)
-               die("index file smaller than expected");
+               die(_("%s: index file smaller than expected"), path);
 
        mmap = xmmap(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, 0);
        if (mmap == MAP_FAILED)
-               die_errno("unable to map index file");
+               die_errno(_("%s: unable to map index file"), path);
        close(fd);
 
        hdr = (const struct cache_header *)mmap;
@@ -2235,7 +2236,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
 
 unmap:
        munmap((void *)mmap, mmap_size);
-       die("index file corrupt");
+       die(_("index file corrupt"));
 }
 
 /*
@@ -2247,7 +2248,7 @@ unmap:
 static void freshen_shared_index(const char *shared_index, int warn)
 {
        if (!check_and_freshen_file(shared_index, 1) && warn)
-               warning("could not freshen shared index '%s'", shared_index);
+               warning(_("could not freshen shared index '%s'"), shared_index);
 }
 
 int read_index_from(struct index_state *istate, const char *path,
@@ -2282,7 +2283,7 @@ int read_index_from(struct index_state *istate, const char *path,
        base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
        ret = do_read_index(split_index->base, base_path, 1);
        if (!oideq(&split_index->base_oid, &split_index->base->oid))
-               die("broken index, expect %s in %s, got %s",
+               die(_("broken index, expect %s in %s, got %s"),
                    base_oid_hex, base_path,
                    oid_to_hex(&split_index->base->oid));
 
@@ -2348,14 +2349,14 @@ void validate_cache_entries(const struct index_state *istate)
 
        for (i = 0; i < istate->cache_nr; i++) {
                if (!istate) {
-                       die("internal error: cache entry is not allocated from expected memory pool");
+                       BUG("cache entry is not allocated from expected memory pool");
                } else if (!istate->ce_mem_pool ||
                        !mem_pool_contains(istate->ce_mem_pool, istate->cache[i])) {
                        if (!istate->split_index ||
                                !istate->split_index->base ||
                                !istate->split_index->base->ce_mem_pool ||
                                !mem_pool_contains(istate->split_index->base->ce_mem_pool, istate->cache[i])) {
-                               die("internal error: cache entry is not allocated from expected memory pool");
+                               BUG("cache entry is not allocated from expected memory pool");
                        }
                }
        }
@@ -3096,7 +3097,7 @@ static int write_shared_index(struct index_state *istate,
                return ret;
        ret = adjust_shared_perm(get_tempfile_path(*temp));
        if (ret) {
-               error("cannot fix permission bits on %s", get_tempfile_path(*temp));
+               error(_("cannot fix permission bits on '%s'"), get_tempfile_path(*temp));
                return ret;
        }
        ret = rename_tempfile(temp,
@@ -3146,7 +3147,7 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
        struct split_index *si = istate->split_index;
 
        if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
-               cache_tree_verify(istate);
+               cache_tree_verify(the_repository, istate);
 
        if ((flags & SKIP_IF_UNCHANGED) && !istate->cache_changed) {
                if (flags & COMMIT_LOCK)
@@ -3243,7 +3244,7 @@ int read_index_unmerged(struct index_state *istate)
                new_ce->ce_namelen = len;
                new_ce->ce_mode = ce->ce_mode;
                if (add_index_entry(istate, new_ce, ADD_CACHE_SKIP_DFCHECK))
-                       return error("%s: cannot drop to stage #0",
+                       return error(_("%s: cannot drop to stage #0"),
                                     new_ce->name);
        }
        return unmerged;
@@ -3494,71 +3495,71 @@ static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context,
 
 static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset)
 {
-       const char *index = NULL;
-       uint32_t extsize, ext_version;
-       struct index_entry_offset_table *ieot;
-       int i, nr;
-
-       /* find the IEOT extension */
-       if (!offset)
-              return NULL;
-       while (offset <= mmap_size - the_hash_algo->rawsz - 8) {
-              extsize = get_be32(mmap + offset + 4);
-              if (CACHE_EXT((mmap + offset)) == CACHE_EXT_INDEXENTRYOFFSETTABLE) {
-                      index = mmap + offset + 4 + 4;
-                      break;
-              }
-              offset += 8;
-              offset += extsize;
-       }
-       if (!index)
-              return NULL;
-
-       /* validate the version is IEOT_VERSION */
-       ext_version = get_be32(index);
-       if (ext_version != IEOT_VERSION) {
-              error("invalid IEOT version %d", ext_version);
-              return NULL;
-       }
-       index += sizeof(uint32_t);
-
-       /* extension size - version bytes / bytes per entry */
-       nr = (extsize - sizeof(uint32_t)) / (sizeof(uint32_t) + sizeof(uint32_t));
-       if (!nr) {
-              error("invalid number of IEOT entries %d", nr);
-              return NULL;
-       }
-       ieot = xmalloc(sizeof(struct index_entry_offset_table)
-              + (nr * sizeof(struct index_entry_offset)));
-       ieot->nr = nr;
-       for (i = 0; i < nr; i++) {
-              ieot->entries[i].offset = get_be32(index);
-              index += sizeof(uint32_t);
-              ieot->entries[i].nr = get_be32(index);
-              index += sizeof(uint32_t);
-       }
-
-       return ieot;
+       const char *index = NULL;
+       uint32_t extsize, ext_version;
+       struct index_entry_offset_table *ieot;
+       int i, nr;
+
+       /* find the IEOT extension */
+       if (!offset)
+               return NULL;
+       while (offset <= mmap_size - the_hash_algo->rawsz - 8) {
+               extsize = get_be32(mmap + offset + 4);
+               if (CACHE_EXT((mmap + offset)) == CACHE_EXT_INDEXENTRYOFFSETTABLE) {
+                       index = mmap + offset + 4 + 4;
+                       break;
+               }
+               offset += 8;
+               offset += extsize;
+       }
+       if (!index)
+               return NULL;
+
+       /* validate the version is IEOT_VERSION */
+       ext_version = get_be32(index);
+       if (ext_version != IEOT_VERSION) {
+               error("invalid IEOT version %d", ext_version);
+               return NULL;
+       }
+       index += sizeof(uint32_t);
+
+       /* extension size - version bytes / bytes per entry */
+       nr = (extsize - sizeof(uint32_t)) / (sizeof(uint32_t) + sizeof(uint32_t));
+       if (!nr) {
+               error("invalid number of IEOT entries %d", nr);
+               return NULL;
+       }
+       ieot = xmalloc(sizeof(struct index_entry_offset_table)
+                      + (nr * sizeof(struct index_entry_offset)));
+       ieot->nr = nr;
+       for (i = 0; i < nr; i++) {
+               ieot->entries[i].offset = get_be32(index);
+               index += sizeof(uint32_t);
+               ieot->entries[i].nr = get_be32(index);
+               index += sizeof(uint32_t);
+       }
+
+       return ieot;
 }
 
 static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot)
 {
-       uint32_t buffer;
-       int i;
+       uint32_t buffer;
+       int i;
 
-       /* version */
-       put_be32(&buffer, IEOT_VERSION);
-       strbuf_add(sb, &buffer, sizeof(uint32_t));
+       /* version */
+       put_be32(&buffer, IEOT_VERSION);
+       strbuf_add(sb, &buffer, sizeof(uint32_t));
 
-       /* ieot */
-       for (i = 0; i < ieot->nr; i++) {
+       /* ieot */
+       for (i = 0; i < ieot->nr; i++) {
 
-              /* offset */
-              put_be32(&buffer, ieot->entries[i].offset);
-              strbuf_add(sb, &buffer, sizeof(uint32_t));
+               /* offset */
+               put_be32(&buffer, ieot->entries[i].offset);
+               strbuf_add(sb, &buffer, sizeof(uint32_t));
 
-              /* count */
-              put_be32(&buffer, ieot->entries[i].nr);
-              strbuf_add(sb, &buffer, sizeof(uint32_t));
-       }
+               /* count */
+               put_be32(&buffer, ieot->entries[i].nr);
+               strbuf_add(sb, &buffer, sizeof(uint32_t));
+       }
 }
index 78f3263fc153079938898989822b57e5888568d6..68aff1dac28878f2f9c010459bf5f4362322bd3f 100644 (file)
@@ -53,7 +53,7 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,
        }
 }
 
-int edit_todo_list(unsigned flags)
+int edit_todo_list(struct repository *r, unsigned flags)
 {
        struct strbuf buf = STRBUF_INIT;
        const char *todo_file = rebase_path_todo();
@@ -69,7 +69,7 @@ int edit_todo_list(unsigned flags)
 
        strbuf_release(&buf);
 
-       transform_todos(flags | TODO_LIST_SHORTEN_IDS);
+       transform_todos(r, flags | TODO_LIST_SHORTEN_IDS);
 
        if (strbuf_read_file(&buf, todo_file, 0) < 0)
                return error_errno(_("could not read '%s'."), todo_file);
@@ -85,7 +85,7 @@ int edit_todo_list(unsigned flags)
        if (launch_sequence_editor(todo_file, NULL, NULL))
                return -1;
 
-       transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS));
+       transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS));
 
        return 0;
 }
index 971da037769476da196ae39eec89431be4923d54..17b6c9f6d05ea2372c58a50679cb0f8fb808aaef 100644 (file)
@@ -1,8 +1,11 @@
 #ifndef REBASE_INTERACTIVE_H
 #define REBASE_INTERACTIVE_H
 
+struct strbuf;
+struct repository;
+
 void append_todo_help(unsigned edit_todo, unsigned keep_empty,
                      struct strbuf *buf);
-int edit_todo_list(unsigned flags);
+int edit_todo_list(struct repository *r, unsigned flags);
 
 #endif
index 5de616befe46e64b014ebe7c3feb5913bc8af2df..422a9c9ae3fd2cfd552ce48c5e68ce18919b1eb9 100644 (file)
@@ -230,13 +230,31 @@ static int objecttype_atom_parser(const struct ref_format *format, struct used_a
 
 static int objectsize_atom_parser(const struct ref_format *format, struct used_atom *atom,
                                  const char *arg, struct strbuf *err)
+{
+       if (!arg) {
+               if (*atom->name == '*')
+                       oi_deref.info.sizep = &oi_deref.size;
+               else
+                       oi.info.sizep = &oi.size;
+       } else if (!strcmp(arg, "disk")) {
+               if (*atom->name == '*')
+                       oi_deref.info.disk_sizep = &oi_deref.disk_size;
+               else
+                       oi.info.disk_sizep = &oi.disk_size;
+       } else
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(objectsize) argument: %s"), arg);
+       return 0;
+}
+
+static int deltabase_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                                const char *arg, struct strbuf *err)
 {
        if (arg)
-               return strbuf_addf_ret(err, -1, _("%%(objectsize) does not take arguments"));
+               return strbuf_addf_ret(err, -1, _("%%(deltabase) does not take arguments"));
        if (*atom->name == '*')
-               oi_deref.info.sizep = &oi_deref.size;
+               oi_deref.info.delta_base_sha1 = oi_deref.delta_base_oid.hash;
        else
-               oi.info.sizep = &oi.size;
+               oi.info.delta_base_sha1 = oi.delta_base_oid.hash;
        return 0;
 }
 
@@ -431,6 +449,7 @@ static struct {
        { "objecttype", SOURCE_OTHER, FIELD_STR, objecttype_atom_parser },
        { "objectsize", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser },
        { "objectname", SOURCE_OTHER, FIELD_STR, objectname_atom_parser },
+       { "deltabase", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },
        { "tree", SOURCE_OBJ },
        { "parent", SOURCE_OBJ },
        { "numparent", SOURCE_OBJ, FIELD_ULONG },
@@ -880,10 +899,14 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
                        name++;
                if (!strcmp(name, "objecttype"))
                        v->s = xstrdup(type_name(oi->type));
-               else if (!strcmp(name, "objectsize")) {
+               else if (!strcmp(name, "objectsize:disk")) {
+                       v->value = oi->disk_size;
+                       v->s = xstrfmt("%"PRIuMAX, (uintmax_t)oi->disk_size);
+               } else if (!strcmp(name, "objectsize")) {
                        v->value = oi->size;
                        v->s = xstrfmt("%"PRIuMAX , (uintmax_t)oi->size);
-               }
+               } else if (!strcmp(name, "deltabase"))
+                       v->s = xstrdup(oid_to_hex(&oi->delta_base_oid));
                else if (deref)
                        grab_objectname(name, &oi->oid, v, &used_atom[i]);
        }
@@ -1417,7 +1440,7 @@ char *get_head_description(void)
        struct strbuf desc = STRBUF_INIT;
        struct wt_status_state state;
        memset(&state, 0, sizeof(state));
-       wt_status_get_state(&state, 1);
+       wt_status_get_state(the_repository, &state, 1);
        if (state.rebase_in_progress ||
            state.rebase_interactive_in_progress) {
                if (state.branch)
@@ -1482,6 +1505,8 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
                                     OBJECT_INFO_LOOKUP_REPLACE))
                return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
                                       oid_to_hex(&oi->oid), ref->refname);
+       if (oi->info.disk_sizep && oi->disk_size < 0)
+               BUG("Object size is less than zero.");
 
        if (oi->info.contentp) {
                *obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten);
@@ -2324,9 +2349,11 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
 
        if (rf->merge) {
                if (no_merged) {
-                       return opterror(opt, "is incompatible with --merged", 0);
+                       return error(_("option `%s' is incompatible with --merged"),
+                                    opt->long_name);
                } else {
-                       return opterror(opt, "is incompatible with --no-merged", 0);
+                       return error(_("option `%s' is incompatible with --no-merged"),
+                                    opt->long_name);
                }
        }
 
@@ -2340,7 +2367,7 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
        rf->merge_commit = lookup_commit_reference_gently(the_repository,
                                                          &oid, 0);
        if (!rf->merge_commit)
-               return opterror(opt, "must point to a commit", 0);
+               return error(_("option `%s' must point to a commit"), opt->long_name);
 
        return 0;
 }
index 1220dffcdc57a17476fb2021db5fb1605857ab80..2e04d53ac8e79c9f5a3839a7ab97fd67f2588021 100644 (file)
@@ -204,7 +204,8 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
 
        packet_reader_init(&reader, -1, heads->buf, heads->len,
                           PACKET_READ_CHOMP_NEWLINE |
-                          PACKET_READ_GENTLE_ON_EOF);
+                          PACKET_READ_GENTLE_ON_EOF |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
 
        heads->version = discover_version(&reader);
        switch (heads->version) {
@@ -380,7 +381,6 @@ static struct discovery *discover_refs(const char *service, int for_push)
        http_options.extra_headers = &extra_headers;
        http_options.initial_request = 1;
        http_options.no_cache = 1;
-       http_options.keep_error = 1;
 
        http_ret = http_get_strbuf(refs_url.buf, &buffer, &http_options);
        switch (http_ret) {
@@ -409,28 +409,37 @@ static struct discovery *discover_refs(const char *service, int for_push)
        if (maybe_smart &&
            (5 <= last->len && last->buf[4] == '#') &&
            !strbuf_cmp(&exp, &type)) {
-               char *line;
+               struct packet_reader reader;
+               packet_reader_init(&reader, -1, last->buf, last->len,
+                                  PACKET_READ_CHOMP_NEWLINE |
+                                  PACKET_READ_DIE_ON_ERR_PACKET);
 
                /*
                 * smart HTTP response; validate that the service
                 * pkt-line matches our request.
                 */
-               line = packet_read_line_buf(&last->buf, &last->len, NULL);
-               if (!line)
+               if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
                        die("invalid server response; expected service, got flush packet");
 
                strbuf_reset(&exp);
                strbuf_addf(&exp, "# service=%s", service);
-               if (strcmp(line, exp.buf))
-                       die("invalid server response; got '%s'", line);
+               if (strcmp(reader.line, exp.buf))
+                       die("invalid server response; got '%s'", reader.line);
                strbuf_release(&exp);
 
                /* The header can include additional metadata lines, up
                 * until a packet flush marker.  Ignore these now, but
                 * in the future we might start to scan them.
                 */
-               while (packet_read_line_buf(&last->buf, &last->len, NULL))
-                       ;
+               for (;;) {
+                       packet_reader_read(&reader);
+                       if (reader.pktlen <= 0) {
+                               break;
+                       }
+               }
+
+               last->buf = reader.src_buffer;
+               last->len = reader.src_len;
 
                last->proto_git = 1;
        } else if (maybe_smart &&
@@ -546,14 +555,30 @@ static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
 }
 #endif
 
+struct rpc_in_data {
+       struct rpc_state *rpc;
+       struct active_request_slot *slot;
+};
+
+/*
+ * A callback for CURLOPT_WRITEFUNCTION. The return value is the bytes consumed
+ * from ptr.
+ */
 static size_t rpc_in(char *ptr, size_t eltsize,
                size_t nmemb, void *buffer_)
 {
        size_t size = eltsize * nmemb;
-       struct rpc_state *rpc = buffer_;
+       struct rpc_in_data *data = buffer_;
+       long response_code;
+
+       if (curl_easy_getinfo(data->slot->curl, CURLINFO_RESPONSE_CODE,
+                             &response_code) != CURLE_OK)
+               return size;
+       if (response_code >= 300)
+               return size;
        if (size)
-               rpc->any_written = 1;
-       write_or_die(rpc->in, ptr, size);
+               data->rpc->any_written = 1;
+       write_or_die(data->rpc->in, ptr, size);
        return size;
 }
 
@@ -617,7 +642,8 @@ static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
        return err;
 }
 
-static curl_off_t xcurl_off_t(size_t len) {
+static curl_off_t xcurl_off_t(size_t len)
+{
        uintmax_t size = len;
        if (size > maximum_signed_value_of_type(curl_off_t))
                die("cannot handle pushes this big");
@@ -633,6 +659,7 @@ static int post_rpc(struct rpc_state *rpc)
        size_t gzip_size = 0;
        int err, large_request = 0;
        int needs_100_continue = 0;
+       struct rpc_in_data rpc_in_data;
 
        /* Try to load the entire request, if we can fit it into the
         * allocated buffer space we can use HTTP/1.0 and avoid the
@@ -765,7 +792,10 @@ retry:
 
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
+       rpc_in_data.rpc = rpc;
+       rpc_in_data.slot = slot;
+       curl_easy_setopt(slot->curl, CURLOPT_FILE, &rpc_in_data);
+       curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
 
 
        rpc->any_written = 0;
@@ -1174,7 +1204,8 @@ static void proxy_state_init(struct proxy_state *p, const char *service_name,
                p->headers = curl_slist_append(p->headers, buf.buf);
 
        packet_reader_init(&p->reader, p->in, NULL, 0,
-                          PACKET_READ_GENTLE_ON_EOF);
+                          PACKET_READ_GENTLE_ON_EOF |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
 
        strbuf_release(&buf);
 }
index b850f2feb34d41ca8c23d237f0acc52896530daa..9cc3b07d214a63271849243c5f823b8f87ad631b 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -13,6 +13,7 @@
 #include "mergesort.h"
 #include "argv-array.h"
 #include "commit-reach.h"
+#include "advice.h"
 
 enum map_direction { FROM_SRC, FROM_DST };
 
@@ -336,14 +337,14 @@ static int handle_config(const char *key, const char *value, void *cb)
                if (!name)
                        return 0;
                if (!strcmp(subkey, "insteadof")) {
-                       rewrite = make_rewrite(&rewrites, name, namelen);
                        if (!value)
                                return config_error_nonbool(key);
+                       rewrite = make_rewrite(&rewrites, name, namelen);
                        add_instead_of(rewrite, xstrdup(value));
                } else if (!strcmp(subkey, "pushinsteadof")) {
-                       rewrite = make_rewrite(&rewrites_push, name, namelen);
                        if (!value)
                                return config_error_nonbool(key);
+                       rewrite = make_rewrite(&rewrites_push, name, namelen);
                        add_instead_of(rewrite, xstrdup(value));
                }
        }
@@ -359,7 +360,7 @@ static int handle_config(const char *key, const char *value, void *cb)
                return 0;
        /* Handle remote.<name>.* variables */
        if (*name == '/') {
-               warning("Config remote shorthand cannot begin with '/': %s",
+               warning(_("config remote shorthand cannot begin with '/': %s"),
                        name);
                return 0;
        }
@@ -406,7 +407,7 @@ static int handle_config(const char *key, const char *value, void *cb)
                if (!remote->receivepack)
                        remote->receivepack = v;
                else
-                       error("more than one receivepack given, using the first");
+                       error(_("more than one receivepack given, using the first"));
        } else if (!strcmp(subkey, "uploadpack")) {
                const char *v;
                if (git_config_string(&v, key, value))
@@ -414,7 +415,7 @@ static int handle_config(const char *key, const char *value, void *cb)
                if (!remote->uploadpack)
                        remote->uploadpack = v;
                else
-                       error("more than one uploadpack given, using the first");
+                       error(_("more than one uploadpack given, using the first"));
        } else if (!strcmp(subkey, "tagopt")) {
                if (!strcmp(value, "--no-tags"))
                        remote->fetch_tags = -1;
@@ -620,7 +621,7 @@ static void handle_duplicate(struct ref *ref1, struct ref *ref2)
                         * FETCH_HEAD_IGNORE entries always appear at
                         * the end of the list.
                         */
-                       die(_("Internal error"));
+                       BUG("Internal error");
                }
        }
        free(ref2->peer_ref);
@@ -680,7 +681,7 @@ static int match_name_with_pattern(const char *key, const char *name,
        size_t namelen;
        int ret;
        if (!kstar)
-               die("Key '%s' of pattern had no '*'", key);
+               die(_("key '%s' of pattern had no '*'"), key);
        klen = kstar - key;
        ksuffixlen = strlen(kstar + 1);
        namelen = strlen(name);
@@ -690,7 +691,7 @@ static int match_name_with_pattern(const char *key, const char *name,
                struct strbuf sb = STRBUF_INIT;
                const char *vstar = strchr(value, '*');
                if (!vstar)
-                       die("Value '%s' of pattern has no '*'", value);
+                       die(_("value '%s' of pattern has no '*'"), value);
                strbuf_add(&sb, value, vstar - value);
                strbuf_add(&sb, name + klen, namelen - klen - ksuffixlen);
                strbuf_addstr(&sb, vstar + 1);
@@ -707,7 +708,7 @@ static void query_refspecs_multiple(struct refspec *rs,
        int find_src = !query->src;
 
        if (find_src && !query->dst)
-               error("query_refspecs_multiple: need either src or dst");
+               BUG("query_refspecs_multiple: need either src or dst");
 
        for (i = 0; i < rs->nr; i++) {
                struct refspec_item *refspec = &rs->items[i];
@@ -735,7 +736,7 @@ int query_refspecs(struct refspec *rs, struct refspec_item *query)
        char **result = find_src ? &query->src : &query->dst;
 
        if (find_src && !query->dst)
-               return error("query_refspecs: need either src or dst");
+               BUG("query_refspecs: need either src or dst");
 
        for (i = 0; i < rs->nr; i++) {
                struct refspec_item *refspec = &rs->items[i];
@@ -968,12 +969,13 @@ static char *guess_ref(const char *name, struct ref *peer)
        if (!r)
                return NULL;
 
-       if (starts_with(r, "refs/heads/"))
+       if (starts_with(r, "refs/heads/")) {
                strbuf_addstr(&buf, "refs/heads/");
-       else if (starts_with(r, "refs/tags/"))
+       } else if (starts_with(r, "refs/tags/")) {
                strbuf_addstr(&buf, "refs/tags/");
-       else
+       } else {
                return NULL;
+       }
 
        strbuf_addstr(&buf, name);
        return strbuf_detach(&buf, NULL);
@@ -995,12 +997,68 @@ static int match_explicit_lhs(struct ref *src,
                 * way to delete 'other' ref at the remote end.
                 */
                if (try_explicit_object_name(rs->src, match) < 0)
-                       return error("src refspec %s does not match any.", rs->src);
+                       return error(_("src refspec %s does not match any"), rs->src);
                if (allocated_match)
                        *allocated_match = 1;
                return 0;
        default:
-               return error("src refspec %s matches more than one.", rs->src);
+               return error(_("src refspec %s matches more than one"), rs->src);
+       }
+}
+
+static void show_push_unqualified_ref_name_error(const char *dst_value,
+                                                const char *matched_src_name)
+{
+       struct object_id oid;
+       enum object_type type;
+
+       /*
+        * TRANSLATORS: "matches '%s'%" is the <dst> part of "git push
+        * <remote> <src>:<dst>" push, and "being pushed ('%s')" is
+        * the <src>.
+        */
+       error(_("The destination you provided is not a full refname (i.e.,\n"
+               "starting with \"refs/\"). We tried to guess what you meant by:\n"
+               "\n"
+               "- Looking for a ref that matches '%s' on the remote side.\n"
+               "- Checking if the <src> being pushed ('%s')\n"
+               "  is a ref in \"refs/{heads,tags}/\". If so we add a corresponding\n"
+               "  refs/{heads,tags}/ prefix on the remote side.\n"
+               "\n"
+               "Neither worked, so we gave up. You must fully qualify the ref."),
+             dst_value, matched_src_name);
+
+       if (!advice_push_unqualified_ref_name)
+               return;
+
+       if (get_oid(matched_src_name, &oid))
+               BUG("'%s' is not a valid object, "
+                   "match_explicit_lhs() should catch this!",
+                   matched_src_name);
+       type = oid_object_info(the_repository, &oid, NULL);
+       if (type == OBJ_COMMIT) {
+               advise(_("The <src> part of the refspec is a commit object.\n"
+                        "Did you mean to create a new branch by pushing to\n"
+                        "'%s:refs/heads/%s'?"),
+                      matched_src_name, dst_value);
+       } else if (type == OBJ_TAG) {
+               advise(_("The <src> part of the refspec is a tag object.\n"
+                        "Did you mean to create a new tag by pushing to\n"
+                        "'%s:refs/tags/%s'?"),
+                      matched_src_name, dst_value);
+       } else if (type == OBJ_TREE) {
+               advise(_("The <src> part of the refspec is a tree object.\n"
+                        "Did you mean to tag a new tree by pushing to\n"
+                        "'%s:refs/tags/%s'?"),
+                      matched_src_name, dst_value);
+       } else if (type == OBJ_BLOB) {
+               advise(_("The <src> part of the refspec is a blob object.\n"
+                        "Did you mean to tag a new blob by pushing to\n"
+                        "'%s:refs/tags/%s'?"),
+                      matched_src_name, dst_value);
+       } else {
+               BUG("'%s' should be commit/tag/tree/blob, is '%d'",
+                   matched_src_name, type);
        }
 }
 
@@ -1030,7 +1088,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
                if (!dst_value ||
                    ((flag & REF_ISSYMREF) &&
                     !starts_with(dst_value, "refs/heads/")))
-                       die("%s cannot be resolved to branch.",
+                       die(_("%s cannot be resolved to branch"),
                            matched_src->name);
        }
 
@@ -1038,33 +1096,30 @@ static int match_explicit(struct ref *src, struct ref *dst,
        case 1:
                break;
        case 0:
-               if (starts_with(dst_value, "refs/"))
+               if (starts_with(dst_value, "refs/")) {
                        matched_dst = make_linked_ref(dst_value, dst_tail);
-               else if (is_null_oid(&matched_src->new_oid))
-                       error("unable to delete '%s': remote ref does not exist",
+               } else if (is_null_oid(&matched_src->new_oid)) {
+                       error(_("unable to delete '%s': remote ref does not exist"),
                              dst_value);
-               else if ((dst_guess = guess_ref(dst_value, matched_src))) {
+               else if ((dst_guess = guess_ref(dst_value, matched_src))) {
                        matched_dst = make_linked_ref(dst_guess, dst_tail);
                        free(dst_guess);
-               } else
-                       error("unable to push to unqualified destination: %s\n"
-                             "The destination refspec neither matches an "
-                             "existing ref on the remote nor\n"
-                             "begins with refs/, and we are unable to "
-                             "guess a prefix based on the source ref.",
-                             dst_value);
+               } else {
+                       show_push_unqualified_ref_name_error(dst_value,
+                                                            matched_src->name);
+               }
                break;
        default:
                matched_dst = NULL;
-               error("dst refspec %s matches more than one.",
+               error(_("dst refspec %s matches more than one"),
                      dst_value);
                break;
        }
        if (!matched_dst)
                return -1;
        if (matched_dst->peer_ref)
-               return error("dst ref %s receives from more than one src.",
-                     matched_dst->name);
+               return error(_("dst ref %s receives from more than one src"),
+                            matched_dst->name);
        else {
                matched_dst->peer_ref = allocated_src ?
                                        matched_src :
@@ -1782,7 +1837,7 @@ int get_fetch_map(const struct ref *remote_refs,
                        ref_map = get_remote_ref(remote_refs, name);
                }
                if (!missing_ok && !ref_map)
-                       die("Couldn't find remote ref %s", name);
+                       die(_("couldn't find remote ref %s"), name);
                if (ref_map) {
                        ref_map->peer_ref = get_local_ref(refspec->dst);
                        if (ref_map->peer_ref && refspec->force)
@@ -1795,7 +1850,7 @@ int get_fetch_map(const struct ref *remote_refs,
                        if (!starts_with((*rmp)->peer_ref->name, "refs/") ||
                            check_refname_format((*rmp)->peer_ref->name, 0)) {
                                struct ref *ignore = *rmp;
-                               error("* Ignoring funny ref '%s' locally",
+                               error(_("* Ignoring funny ref '%s' locally"),
                                      (*rmp)->peer_ref->name);
                                *rmp = (*rmp)->next;
                                free(ignore->peer_ref);
@@ -1890,7 +1945,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
        repo_init_revisions(the_repository, &revs, NULL);
        setup_revisions(argv.argc, argv.argv, &revs, NULL);
        if (prepare_revision_walk(&revs))
-               die("revision walk setup failed");
+               die(_("revision walk setup failed"));
 
        /* ... and count the commits on each side. */
        while (1) {
@@ -2163,7 +2218,8 @@ static int parse_push_cas_option(struct push_cas_option *cas, const char *arg, i
        else if (!colon[1])
                oidclr(&entry->expect);
        else if (get_oid(colon + 1, &entry->expect))
-               return error("cannot parse expected object name '%s'", colon + 1);
+               return error(_("cannot parse expected object name '%s'"),
+                            colon + 1);
        return 0;
 }
 
index 5dd148671811b383a64b8f3adab87804cea9db08..20c509a9226645b1890c8433b22a7182699fc847 100644 (file)
@@ -63,8 +63,14 @@ void repo_set_gitdir(struct repository *repo,
        free(old_gitdir);
 
        repo_set_commondir(repo, o->commondir);
-       expand_base_dir(&repo->objects->objectdir, o->object_dir,
+
+       if (!repo->objects->odb) {
+               repo->objects->odb = xcalloc(1, sizeof(*repo->objects->odb));
+               repo->objects->odb_tail = &repo->objects->odb->next;
+       }
+       expand_base_dir(&repo->objects->odb->path, o->object_dir,
                        repo->commondir, "objects");
+
        free(repo->objects->alternate_db);
        repo->objects->alternate_db = xstrdup_or_null(o->alternate_db);
        expand_base_dir(&repo->graft_file, o->graft_file,
@@ -166,30 +172,23 @@ error:
        return -1;
 }
 
-/*
- * Initialize 'submodule' as the submodule given by 'path' in parent repository
- * 'superproject'.
- * Return 0 upon success and a non-zero value upon failure.
- */
-int repo_submodule_init(struct repository *submodule,
+int repo_submodule_init(struct repository *subrepo,
                        struct repository *superproject,
-                       const char *path)
+                       const struct submodule *sub)
 {
-       const struct submodule *sub;
        struct strbuf gitdir = STRBUF_INIT;
        struct strbuf worktree = STRBUF_INIT;
        int ret = 0;
 
-       sub = submodule_from_path(superproject, &null_oid, path);
        if (!sub) {
                ret = -1;
                goto out;
        }
 
-       strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path);
-       strbuf_repo_worktree_path(&worktree, superproject, "%s", path);
+       strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", sub->path);
+       strbuf_repo_worktree_path(&worktree, superproject, "%s", sub->path);
 
-       if (repo_init(submodule, gitdir.buf, worktree.buf)) {
+       if (repo_init(subrepo, gitdir.buf, worktree.buf)) {
                /*
                 * If initilization fails then it may be due to the submodule
                 * not being populated in the superproject's worktree.  Instead
@@ -201,16 +200,16 @@ int repo_submodule_init(struct repository *submodule,
                strbuf_repo_git_path(&gitdir, superproject,
                                     "modules/%s", sub->name);
 
-               if (repo_init(submodule, gitdir.buf, NULL)) {
+               if (repo_init(subrepo, gitdir.buf, NULL)) {
                        ret = -1;
                        goto out;
                }
        }
 
-       submodule->submodule_prefix = xstrfmt("%s%s/",
-                                             superproject->submodule_prefix ?
-                                             superproject->submodule_prefix :
-                                             "", path);
+       subrepo->submodule_prefix = xstrfmt("%s%s/",
+                                           superproject->submodule_prefix ?
+                                           superproject->submodule_prefix :
+                                           "", sub->path);
 
 out:
        strbuf_release(&gitdir);
index 9f16c42c1ed04af3bf2e7767c4fd6c05b28ccf9c..0e482b7d49e82c3cf46d81523e057e3a02d2e8f4 100644 (file)
@@ -116,9 +116,17 @@ void repo_set_worktree(struct repository *repo, const char *path);
 void repo_set_hash_algo(struct repository *repo, int algo);
 void initialize_the_repository(void);
 int repo_init(struct repository *r, const char *gitdir, const char *worktree);
-int repo_submodule_init(struct repository *submodule,
+
+/*
+ * Initialize the repository 'subrepo' as the submodule given by the
+ * struct submodule 'sub' in parent repository 'superproject'.
+ * Return 0 upon success and a non-zero value upon failure, which may happen
+ * if the submodule is not found, or 'sub' is NULL.
+ */
+struct submodule;
+int repo_submodule_init(struct repository *subrepo,
                        struct repository *superproject,
-                       const char *path);
+                       const struct submodule *sub);
 void repo_clear(struct repository *repo);
 
 /*
index b5b2357411f145867eb1918cbc79f77c6192db1a..13624038e66c2084c478711599ed9f005cf2e53d 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -198,10 +198,10 @@ static struct rerere_id *new_rerere_id(unsigned char *sha1)
  * work on (i.e. what is left by the previous invocation of "git
  * rerere" during the current conflict resolution session).
  */
-static void read_rr(struct string_list *rr)
+static void read_rr(struct repository *r, struct string_list *rr)
 {
        struct strbuf buf = STRBUF_INIT;
-       FILE *in = fopen_or_warn(git_path_merge_rr(the_repository), "r");
+       FILE *in = fopen_or_warn(git_path_merge_rr(r), "r");
 
        if (!in)
                return;
@@ -593,7 +593,7 @@ int rerere_remaining(struct repository *r, struct string_list *merge_rr)
 {
        int i;
 
-       if (setup_rerere(merge_rr, RERERE_READONLY))
+       if (setup_rerere(r, merge_rr, RERERE_READONLY))
                return 0;
        if (read_index(r->index) < 0)
                return error(_("index file corrupt"));
@@ -882,7 +882,7 @@ static int is_rerere_enabled(void)
        return 1;
 }
 
-int setup_rerere(struct string_list *merge_rr, int flags)
+int setup_rerere(struct repository *r, struct string_list *merge_rr, int flags)
 {
        int fd;
 
@@ -896,9 +896,9 @@ int setup_rerere(struct string_list *merge_rr, int flags)
                fd = 0;
        else
                fd = hold_lock_file_for_update(&write_lock,
-                                              git_path_merge_rr(the_repository),
+                                              git_path_merge_rr(r),
                                               LOCK_DIE_ON_ERROR);
-       read_rr(merge_rr);
+       read_rr(r, merge_rr);
        return fd;
 }
 
@@ -912,7 +912,7 @@ int repo_rerere(struct repository *r, int flags)
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
        int fd, status;
 
-       fd = setup_rerere(&merge_rr, flags);
+       fd = setup_rerere(r, &merge_rr, flags);
        if (fd < 0)
                return 0;
        status = do_plain_rerere(r, &merge_rr, fd);
@@ -1110,7 +1110,7 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec)
        if (read_index(r->index) < 0)
                return error(_("index file corrupt"));
 
-       fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
+       fd = setup_rerere(r, &merge_rr, RERERE_NOAUTOUPDATE);
        if (fd < 0)
                return 0;
 
@@ -1178,7 +1178,7 @@ static void prune_one(struct rerere_id *id,
                unlink_rr_item(id);
 }
 
-void rerere_gc(struct string_list *rr)
+void rerere_gc(struct repository *r, struct string_list *rr)
 {
        struct string_list to_remove = STRING_LIST_INIT_DUP;
        DIR *dir;
@@ -1188,7 +1188,7 @@ void rerere_gc(struct string_list *rr)
        timestamp_t cutoff_noresolve = now - 15 * 86400;
        timestamp_t cutoff_resolve = now - 60 * 86400;
 
-       if (setup_rerere(rr, 0) < 0)
+       if (setup_rerere(r, rr, 0) < 0)
                return;
 
        git_config_get_expiry_in_days("gc.rerereresolved", &cutoff_resolve, now);
@@ -1236,11 +1236,11 @@ void rerere_gc(struct string_list *rr)
  *
  * NEEDSWORK: shouldn't we be calling this from "reset --hard"?
  */
-void rerere_clear(struct string_list *merge_rr)
+void rerere_clear(struct repository *r, struct string_list *merge_rr)
 {
        int i;
 
-       if (setup_rerere(merge_rr, 0) < 0)
+       if (setup_rerere(r, merge_rr, 0) < 0)
                return;
 
        for (i = 0; i < merge_rr->nr; i++) {
@@ -1250,6 +1250,6 @@ void rerere_clear(struct string_list *merge_rr)
                        rmdir(rerere_path(id, NULL));
                }
        }
-       unlink_or_warn(git_path_merge_rr(the_repository));
+       unlink_or_warn(git_path_merge_rr(r));
        rollback_lock_file(&write_lock);
 }
index 5ad8864b3c71625a7fae5f67e625793d73e40fe4..c32d79c3bd81f1ff6e9247b77a050784b917ecfd 100644 (file)
--- a/rerere.h
+++ b/rerere.h
@@ -23,7 +23,7 @@ struct rerere_id {
        int variant;
 };
 
-int setup_rerere(struct string_list *, int);
+int setup_rerere(struct repository *,struct string_list *, int);
 #ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
 #define rerere(flags) repo_rerere(the_repository, flags)
 #endif
@@ -37,8 +37,8 @@ int repo_rerere(struct repository *, int);
 const char *rerere_path(const struct rerere_id *, const char *file);
 int rerere_forget(struct repository *, struct pathspec *);
 int rerere_remaining(struct repository *, struct string_list *);
-void rerere_clear(struct string_list *);
-void rerere_gc(struct string_list *);
+void rerere_clear(struct repository *, struct string_list *);
+void rerere_gc(struct repository *, struct string_list *);
 
 #define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \
        N_("update the index with reused conflict resolution if possible"))
index 13e0519c0241635c0c1fd14a7ff12e9478c70bd4..8f886fe8c644bfce221ddd2be94db7f05b5451e7 100644 (file)
@@ -67,10 +67,10 @@ static void mark_tree_contents_uninteresting(struct repository *r,
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
-                       mark_tree_uninteresting(r, lookup_tree(r, entry.oid));
+                       mark_tree_uninteresting(r, lookup_tree(r, &entry.oid));
                        break;
                case OBJ_BLOB:
-                       mark_blob_uninteresting(lookup_blob(r, entry.oid));
+                       mark_blob_uninteresting(lookup_blob(r, &entry.oid));
                        break;
                default:
                        /* Subproject commit - not in this repository */
@@ -213,7 +213,20 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
 {
        struct object *object;
 
-       object = parse_object(revs->repo, oid);
+       /*
+        * If the repository has commit graphs, repo_parse_commit() avoids
+        * reading the object buffer, so use it whenever possible.
+        */
+       if (oid_object_info(revs->repo, oid, NULL) == OBJ_COMMIT) {
+               struct commit *c = lookup_commit(revs->repo, oid);
+               if (!repo_parse_commit(revs->repo, c))
+                       object = (struct object *) c;
+               else
+                       object = NULL;
+       } else {
+               object = parse_object(revs->repo, oid);
+       }
+
        if (!object) {
                if (revs->ignore_missing)
                        return object;
@@ -1463,6 +1476,7 @@ void repo_init_revisions(struct repository *r,
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
+       revs->pruning.repo = r;
        revs->pruning.flags.recursive = 1;
        revs->pruning.flags.quick = 1;
        revs->pruning.add_remove = file_add_remove;
@@ -1495,8 +1509,8 @@ void repo_init_revisions(struct repository *r,
 }
 
 static void add_pending_commit_list(struct rev_info *revs,
-                                    struct commit_list *commit_list,
-                                    unsigned int flags)
+                                   struct commit_list *commit_list,
+                                   unsigned int flags)
 {
        while (commit_list) {
                struct object *object = &commit_list->item->object;
@@ -1729,6 +1743,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, &oid, flags ^ local_flags);
+       if (!object)
+               return revs->ignore_missing ? 0 : -1;
        add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
        add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
        free(oc.path);
@@ -1791,7 +1807,8 @@ static void add_message_grep(struct rev_info *revs, const char *pattern)
 }
 
 static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
-                              int *unkc, const char **unkv)
+                              int *unkc, const char **unkv,
+                              const struct setup_revision_opt* opt)
 {
        const char *arg = argv[0];
        const char *optarg;
@@ -2151,7 +2168,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->limited = 1;
        } else if (!strcmp(arg, "--ignore-missing")) {
                revs->ignore_missing = 1;
-       } else if (revs->allow_exclude_promisor_objects_opt &&
+       } else if (opt && opt->allow_exclude_promisor_objects &&
                   !strcmp(arg, "--exclude-promisor-objects")) {
                if (fetch_if_missing)
                        BUG("exclude_promisor_objects can only be used when fetch_if_missing is 0");
@@ -2173,7 +2190,7 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
                        const char * const usagestr[])
 {
        int n = handle_revision_opt(revs, ctx->argc, ctx->argv,
-                                   &ctx->cpidx, ctx->out);
+                                   &ctx->cpidx, ctx->out, NULL);
        if (n <= 0) {
                error("unknown option `%s'", ctx->argv[0]);
                usage_with_options(usagestr, options);
@@ -2391,7 +2408,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                                continue;
                        }
 
-                       opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv);
+                       opts = handle_revision_opt(revs, argc - i, argv + i,
+                                                  &left, argv, opt);
                        if (opts > 0) {
                                i += opts - 1;
                                continue;
index 7987bfcd2e9bd6ee7bac4f1cbeb10af17ab40b50..52e5a88ff5725862dced5c72fbc2aa6435b94e3b 100644 (file)
@@ -161,7 +161,6 @@ struct rev_info {
                        do_not_die_on_missing_tree:1,
 
                        /* for internal use only */
-                       allow_exclude_promisor_objects_opt:1,
                        exclude_promisor_objects:1;
 
        /* Diff flags */
@@ -297,7 +296,8 @@ struct setup_revision_opt {
        const char *def;
        void (*tweak)(struct rev_info *, struct setup_revision_opt *);
        const char *submodule;  /* TODO: drop this and use rev_info->repo */
-       int assume_dashdash;
+       unsigned int    assume_dashdash:1,
+                       allow_exclude_promisor_objects:1;
        unsigned revarg_opt;
 };
 
index f692686770f69b49d44bf6dbabdb17886d85ea1a..7b9829f165e7aff7abe804fb780cf7c9d88aaf73 100644 (file)
@@ -135,38 +135,36 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
        return 0;
 }
 
-static int receive_unpack_status(int in)
+static int receive_unpack_status(struct packet_reader *reader)
 {
-       const char *line = packet_read_line(in, NULL);
-       if (!line)
+       if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                return error(_("unexpected flush packet while reading remote unpack status"));
-       if (!skip_prefix(line, "unpack ", &line))
-               return error(_("unable to parse remote unpack status: %s"), line);
-       if (strcmp(line, "ok"))
-               return error(_("remote unpack failed: %s"), line);
+       if (!skip_prefix(reader->line, "unpack ", &reader->line))
+               return error(_("unable to parse remote unpack status: %s"), reader->line);
+       if (strcmp(reader->line, "ok"))
+               return error(_("remote unpack failed: %s"), reader->line);
        return 0;
 }
 
-static int receive_status(int in, struct ref *refs)
+static int receive_status(struct packet_reader *reader, struct ref *refs)
 {
        struct ref *hint;
        int ret;
 
        hint = NULL;
-       ret = receive_unpack_status(in);
+       ret = receive_unpack_status(reader);
        while (1) {
-               char *refname;
+               const char *refname;
                char *msg;
-               char *line = packet_read_line(in, NULL);
-               if (!line)
+               if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                        break;
-               if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) {
-                       error("invalid ref status from remote: %s", line);
+               if (!starts_with(reader->line, "ok ") && !starts_with(reader->line, "ng ")) {
+                       error("invalid ref status from remote: %s", reader->line);
                        ret = -1;
                        break;
                }
 
-               refname = line + 3;
+               refname = reader->line + 3;
                msg = strchr(refname, ' ');
                if (msg)
                        *msg++ = '\0';
@@ -187,7 +185,7 @@ static int receive_status(int in, struct ref *refs)
                        continue;
                }
 
-               if (line[0] == 'o' && line[1] == 'k')
+               if (reader->line[0] == 'o' && reader->line[1] == 'k')
                        hint->status = REF_STATUS_OK;
                else {
                        hint->status = REF_STATUS_REMOTE_REJECT;
@@ -390,6 +388,7 @@ int send_pack(struct send_pack_args *args,
        int ret;
        struct async demux;
        const char *push_cert_nonce = NULL;
+       struct packet_reader reader;
 
        /* Does the other end support the reporting? */
        if (server_supports("report-status"))
@@ -559,6 +558,10 @@ int send_pack(struct send_pack_args *args,
                in = demux.out;
        }
 
+       packet_reader_init(&reader, in, NULL, 0,
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
+
        if (need_pack_data && cmds_sent) {
                if (pack_objects(out, remote_refs, extra_have, args) < 0) {
                        for (ref = remote_refs; ref; ref = ref->next)
@@ -573,7 +576,7 @@ int send_pack(struct send_pack_args *args,
                         * are failing, and just want the error() side effects.
                         */
                        if (status_report)
-                               receive_unpack_status(in);
+                               receive_unpack_status(&reader);
 
                        if (use_sideband) {
                                close(demux.out);
@@ -590,7 +593,7 @@ int send_pack(struct send_pack_args *args,
                packet_flush(out);
 
        if (status_report && cmds_sent)
-               ret = receive_status(in, remote_refs);
+               ret = receive_status(&reader, remote_refs);
        else
                ret = 0;
        if (args->stateless_rpc)
index e1a4dd15f1a826c7bd1bf4780c8f85c21117c43b..a6333abd3990825ed0573c408837dbf164f31f4f 100644 (file)
@@ -150,6 +150,7 @@ static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs-to-delete")
 static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
 static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
 static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose")
+static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
 static GIT_PATH_FUNC(rebase_path_signoff, "rebase-merge/signoff")
 static GIT_PATH_FUNC(rebase_path_head_name, "rebase-merge/head-name")
 static GIT_PATH_FUNC(rebase_path_onto, "rebase-merge/onto")
@@ -157,7 +158,7 @@ static GIT_PATH_FUNC(rebase_path_autostash, "rebase-merge/autostash")
 static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
 static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
 static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
-static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
+static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedule-failed-exec")
 
 static int git_sequencer_config(const char *k, const char *v, void *cb)
 {
@@ -356,7 +357,8 @@ static void free_message(struct commit *commit, struct commit_message *msg)
        unuse_commit_buffer(commit, msg->message);
 }
 
-static void print_advice(int show_hint, struct replay_opts *opts)
+static void print_advice(struct repository *r, int show_hint,
+                        struct replay_opts *opts)
 {
        char *msg = getenv("GIT_CHERRY_PICK_HELP");
 
@@ -367,7 +369,7 @@ static void print_advice(int show_hint, struct replay_opts *opts)
                 * (typically rebase --interactive) wants to take care
                 * of the commit itself so remove CHERRY_PICK_HEAD
                 */
-               unlink(git_path_cherry_pick_head(the_repository));
+               unlink(git_path_cherry_pick_head(r));
                return;
        }
 
@@ -440,14 +442,14 @@ static int read_oneliner(struct strbuf *buf,
        return 1;
 }
 
-static struct tree *empty_tree(void)
+static struct tree *empty_tree(struct repository *r)
 {
-       return lookup_tree(the_repository, the_repository->hash_algo->empty_tree);
+       return lookup_tree(r, the_hash_algo->empty_tree);
 }
 
-static int error_dirty_index(struct replay_opts *opts)
+static int error_dirty_index(struct index_state *istate, struct replay_opts *opts)
 {
-       if (read_cache_unmerged())
+       if (read_index_unmerged(istate))
                return error_resolve_conflict(_(action_name(opts)));
 
        error(_("your local changes would be overwritten by %s."),
@@ -472,15 +474,18 @@ static void update_abort_safety_file(void)
                write_file(git_path_abort_safety_file(), "%s", "");
 }
 
-static int fast_forward_to(const struct object_id *to, const struct object_id *from,
-                       int unborn, struct replay_opts *opts)
+static int fast_forward_to(struct repository *r,
+                          const struct object_id *to,
+                          const struct object_id *from,
+                          int unborn,
+                          struct replay_opts *opts)
 {
        struct ref_transaction *transaction;
        struct strbuf sb = STRBUF_INIT;
        struct strbuf err = STRBUF_INIT;
 
-       read_index(&the_index);
-       if (checkout_fast_forward(the_repository, from, to, 1))
+       read_index(r->index);
+       if (checkout_fast_forward(r, from, to, 1))
                return -1; /* the callee should have complained already */
 
        strbuf_addf(&sb, _("%s: fast-forward"), _(action_name(opts)));
@@ -506,24 +511,26 @@ static int fast_forward_to(const struct object_id *to, const struct object_id *f
        return 0;
 }
 
-void append_conflicts_hint(struct strbuf *msgbuf)
+void append_conflicts_hint(struct index_state *istate,
+                          struct strbuf *msgbuf)
 {
        int i;
 
        strbuf_addch(msgbuf, '\n');
        strbuf_commented_addf(msgbuf, "Conflicts:\n");
-       for (i = 0; i < active_nr;) {
-               const struct cache_entry *ce = active_cache[i++];
+       for (i = 0; i < istate->cache_nr;) {
+               const struct cache_entry *ce = istate->cache[i++];
                if (ce_stage(ce)) {
                        strbuf_commented_addf(msgbuf, "\t%s\n", ce->name);
-                       while (i < active_nr && !strcmp(ce->name,
-                                                       active_cache[i]->name))
+                       while (i < istate->cache_nr &&
+                              !strcmp(ce->name, istate->cache[i]->name))
                                i++;
                }
        }
 }
 
-static int do_recursive_merge(struct commit *base, struct commit *next,
+static int do_recursive_merge(struct repository *r,
+                             struct commit *base, struct commit *next,
                              const char *base_label, const char *next_label,
                              struct object_id *head, struct strbuf *msgbuf,
                              struct replay_opts *opts)
@@ -537,7 +544,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        if (hold_locked_index(&index_lock, LOCK_REPORT_ON_ERROR) < 0)
                return -1;
 
-       read_cache();
+       read_index(r->index);
 
        init_merge_options(&o);
        o.ancestor = base ? base_label : "(empty tree)";
@@ -548,8 +555,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        o.show_rename_progress = 1;
 
        head_tree = parse_tree_indirect(head);
-       next_tree = next ? get_commit_tree(next) : empty_tree();
-       base_tree = base ? get_commit_tree(base) : empty_tree();
+       next_tree = next ? get_commit_tree(next) : empty_tree(r);
+       base_tree = base ? get_commit_tree(base) : empty_tree(r);
 
        for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
                parse_merge_opt(&o, *xopt);
@@ -566,7 +573,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
                return clean;
        }
 
-       if (write_locked_index(&the_index, &index_lock,
+       if (write_locked_index(r->index, &index_lock,
                               COMMIT_LOCK | SKIP_IF_UNCHANGED))
                /*
                 * TRANSLATORS: %s will be "revert", "cherry-pick" or
@@ -576,34 +583,35 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
                        _(action_name(opts)));
 
        if (!clean)
-               append_conflicts_hint(msgbuf);
+               append_conflicts_hint(r->index, msgbuf);
 
        return !clean;
 }
 
-static struct object_id *get_cache_tree_oid(void)
+static struct object_id *get_cache_tree_oid(struct index_state *istate)
 {
-       if (!active_cache_tree)
-               active_cache_tree = cache_tree();
+       if (!istate->cache_tree)
+               istate->cache_tree = cache_tree();
 
-       if (!cache_tree_fully_valid(active_cache_tree))
-               if (cache_tree_update(&the_index, 0)) {
+       if (!cache_tree_fully_valid(istate->cache_tree))
+               if (cache_tree_update(istate, 0)) {
                        error(_("unable to update cache tree"));
                        return NULL;
                }
 
-       return &active_cache_tree->oid;
+       return &istate->cache_tree->oid;
 }
 
-static int is_index_unchanged(void)
+static int is_index_unchanged(struct repository *r)
 {
        struct object_id head_oid, *cache_tree_oid;
        struct commit *head_commit;
+       struct index_state *istate = r->index;
 
        if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
                return error(_("could not resolve HEAD commit"));
 
-       head_commit = lookup_commit(the_repository, &head_oid);
+       head_commit = lookup_commit(r, &head_oid);
 
        /*
         * If head_commit is NULL, check_commit, called from
@@ -616,7 +624,7 @@ static int is_index_unchanged(void)
        if (parse_commit(head_commit))
                return -1;
 
-       if (!(cache_tree_oid = get_cache_tree_oid()))
+       if (!(cache_tree_oid = get_cache_tree_oid(istate)))
                return -1;
 
        return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
@@ -888,7 +896,9 @@ static int run_command_silent_on_success(struct child_process *cmd)
  * interactive rebase: in that case, we will want to retain the
  * author metadata.
  */
-static int run_git_commit(const char *defmsg, struct replay_opts *opts,
+static int run_git_commit(struct repository *r,
+                         const char *defmsg,
+                         struct replay_opts *opts,
                          unsigned int flags)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
@@ -911,7 +921,7 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
                if (!defmsg)
                        BUG("root commit without message");
 
-               if (!(cache_tree_oid = get_cache_tree_oid()))
+               if (!(cache_tree_oid = get_cache_tree_oid(r->index)))
                        res = -1;
 
                if (!res)
@@ -1120,7 +1130,9 @@ void commit_post_rewrite(const struct commit *old_head,
        run_rewrite_hook(&old_head->object.oid, new_head);
 }
 
-static int run_prepare_commit_msg_hook(struct strbuf *msg, const char *commit)
+static int run_prepare_commit_msg_hook(struct repository *r,
+                                      struct strbuf *msg,
+                                      const char *commit)
 {
        struct argv_array hook_env = ARGV_ARRAY_INIT;
        int ret;
@@ -1130,7 +1142,7 @@ static int run_prepare_commit_msg_hook(struct strbuf *msg, const char *commit)
        if (write_message(msg->buf, msg->len, name, 0))
                return -1;
 
-       argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", get_index_file());
+       argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", r->index_file);
        argv_array_push(&hook_env, "GIT_EDITOR=:");
        if (commit)
                ret = run_hook_le(hook_env.argv, "prepare-commit-msg", name,
@@ -1186,7 +1198,9 @@ static const char *implicit_ident_advice(void)
 
 }
 
-void print_commit_summary(const char *prefix, const struct object_id *oid,
+void print_commit_summary(struct repository *r,
+                         const char *prefix,
+                         const struct object_id *oid,
                          unsigned int flags)
 {
        struct rev_info rev;
@@ -1197,7 +1211,7 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
        struct strbuf author_ident = STRBUF_INIT;
        struct strbuf committer_ident = STRBUF_INIT;
 
-       commit = lookup_commit(the_repository, oid);
+       commit = lookup_commit(r, oid);
        if (!commit)
                die(_("couldn't look up newly created commit"));
        if (parse_commit(commit))
@@ -1230,7 +1244,7 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
        strbuf_release(&author_ident);
        strbuf_release(&committer_ident);
 
-       repo_init_revisions(the_repository, &rev, prefix);
+       repo_init_revisions(r, &rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
 
        rev.diff = 1;
@@ -1264,7 +1278,7 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
        strbuf_release(&format);
 }
 
-static int parse_head(struct commit **head)
+static int parse_head(struct repository *r, struct commit **head)
 {
        struct commit *current_head;
        struct object_id oid;
@@ -1272,7 +1286,7 @@ static int parse_head(struct commit **head)
        if (get_oid("HEAD", &oid)) {
                current_head = NULL;
        } else {
-               current_head = lookup_commit_reference(the_repository, &oid);
+               current_head = lookup_commit_reference(r, &oid);
                if (!current_head)
                        return error(_("could not parse HEAD"));
                if (!oideq(&oid, &current_head->object.oid)) {
@@ -1296,7 +1310,8 @@ static int parse_head(struct commit **head)
  *   0 - success
  *   1 - run 'git commit'
  */
-static int try_to_commit(struct strbuf *msg, const char *author,
+static int try_to_commit(struct repository *r,
+                        struct strbuf *msg, const char *author,
                         struct replay_opts *opts, unsigned int flags,
                         struct object_id *oid)
 {
@@ -1311,7 +1326,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,
        enum commit_msg_cleanup_mode cleanup;
        int res = 0;
 
-       if (parse_head(&current_head))
+       if (parse_head(r, &current_head))
                return -1;
 
        if (flags & AMEND_MSG) {
@@ -1340,7 +1355,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,
                commit_list_insert(current_head, &parents);
        }
 
-       if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL)) {
+       if (write_index_as_tree(&tree, r->index, r->index_file, 0, NULL)) {
                res = error(_("git write-tree failed to write a tree"));
                goto out;
        }
@@ -1353,7 +1368,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,
        }
 
        if (find_hook("prepare-commit-msg")) {
-               res = run_prepare_commit_msg_hook(msg, hook_commit);
+               res = run_prepare_commit_msg_hook(r, msg, hook_commit);
                if (res)
                        goto out;
                if (strbuf_read_file(&commit_msg, git_path_commit_editmsg(),
@@ -1402,7 +1417,8 @@ out:
        return res;
 }
 
-static int do_commit(const char *msg_file, const char *author,
+static int do_commit(struct repository *r,
+                    const char *msg_file, const char *author,
                     struct replay_opts *opts, unsigned int flags)
 {
        int res = 1;
@@ -1417,20 +1433,20 @@ static int do_commit(const char *msg_file, const char *author,
                                             "from '%s'"),
                                           msg_file);
 
-               res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,
-                                   &oid);
+               res = try_to_commit(r, msg_file ? &sb : NULL,
+                                   author, opts, flags, &oid);
                strbuf_release(&sb);
                if (!res) {
-                       unlink(git_path_cherry_pick_head(the_repository));
-                       unlink(git_path_merge_msg(the_repository));
+                       unlink(git_path_cherry_pick_head(r));
+                       unlink(git_path_merge_msg(r));
                        if (!is_rebase_i(opts))
-                               print_commit_summary(NULL, &oid,
+                               print_commit_summary(r, NULL, &oid,
                                                SUMMARY_SHOW_AUTHOR_DATE);
                        return res;
                }
        }
        if (res == 1)
-               return run_git_commit(msg_file, opts, flags);
+               return run_git_commit(r, msg_file, opts, flags);
 
        return res;
 }
@@ -1458,7 +1474,9 @@ static int is_original_commit_empty(struct commit *commit)
 /*
  * Do we run "git commit" with "--allow-empty"?
  */
-static int allow_empty(struct replay_opts *opts, struct commit *commit)
+static int allow_empty(struct repository *r,
+                      struct replay_opts *opts,
+                      struct commit *commit)
 {
        int index_unchanged, empty_commit;
 
@@ -1475,7 +1493,7 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit)
        if (!opts->allow_empty)
                return 0; /* let "git commit" barf as necessary */
 
-       index_unchanged = is_index_unchanged();
+       index_unchanged = is_index_unchanged(r);
        if (index_unchanged < 0)
                return index_unchanged;
        if (!index_unchanged)
@@ -1579,8 +1597,10 @@ static int is_pick_or_similar(enum todo_command command)
        }
 }
 
-static int update_squash_messages(enum todo_command command,
-               struct commit *commit, struct replay_opts *opts)
+static int update_squash_messages(struct repository *r,
+                                 enum todo_command command,
+                                 struct commit *commit,
+                                 struct replay_opts *opts)
 {
        struct strbuf buf = STRBUF_INIT;
        int res;
@@ -1609,7 +1629,7 @@ static int update_squash_messages(enum todo_command command,
 
                if (get_oid("HEAD", &head))
                        return error(_("need a HEAD to fixup"));
-               if (!(head_commit = lookup_commit_reference(the_repository, &head)))
+               if (!(head_commit = lookup_commit_reference(r, &head)))
                        return error(_("could not read HEAD"));
                if (!(head_message = get_commit_buffer(head_commit, NULL)))
                        return error(_("could not read HEAD's commit message"));
@@ -1670,7 +1690,8 @@ static int update_squash_messages(enum todo_command command,
        return res;
 }
 
-static void flush_rewritten_pending(void) {
+static void flush_rewritten_pending(void)
+{
        struct strbuf buf = STRBUF_INIT;
        struct object_id newoid;
        FILE *out;
@@ -1695,7 +1716,8 @@ static void flush_rewritten_pending(void) {
 }
 
 static void record_in_rewritten(struct object_id *oid,
-               enum todo_command next_command) {
+               enum todo_command next_command)
+{
        FILE *out = fopen_or_warn(rebase_path_rewritten_pending(), "a");
 
        if (!out)
@@ -1708,11 +1730,14 @@ static void record_in_rewritten(struct object_id *oid,
                flush_rewritten_pending();
 }
 
-static int do_pick_commit(enum todo_command command, struct commit *commit,
-               struct replay_opts *opts, int final_fixup)
+static int do_pick_commit(struct repository *r,
+                         enum todo_command command,
+                         struct commit *commit,
+                         struct replay_opts *opts,
+                         int final_fixup)
 {
        unsigned int flags = opts->edit ? EDIT_MSG : 0;
-       const char *msg_file = opts->edit ? NULL : git_path_merge_msg(the_repository);
+       const char *msg_file = opts->edit ? NULL : git_path_merge_msg(r);
        struct object_id head;
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
@@ -1728,7 +1753,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                 * that represents the "current" state for merge-recursive
                 * to work on.
                 */
-               if (write_index_as_tree(&head, &the_index, get_index_file(), 0, NULL))
+               if (write_index_as_tree(&head, r->index, r->index_file, 0, NULL))
                        return error(_("your index file is unmerged."));
        } else {
                unborn = get_oid("HEAD", &head);
@@ -1741,11 +1766,11 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                        unborn = 1;
                } else if (unborn)
                        oidcpy(&head, the_hash_algo->empty_tree);
-               if (index_differs_from(unborn ? empty_tree_oid_hex() : "HEAD",
+               if (index_differs_from(r, unborn ? empty_tree_oid_hex() : "HEAD",
                                       NULL, 0))
-                       return error_dirty_index(opts);
+                       return error_dirty_index(r->index, opts);
        }
-       discard_cache();
+       discard_index(r->index);
 
        if (!commit->parents)
                parent = NULL;
@@ -1766,9 +1791,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                        return error(_("commit %s does not have parent %d"),
                                oid_to_hex(&commit->object.oid), opts->mainline);
                parent = p->item;
-       } else if (0 < opts->mainline)
-               return error(_("mainline was specified but commit %s is not a merge."),
-                       oid_to_hex(&commit->object.oid));
+       } else if (1 < opts->mainline)
+               /*
+                *  Non-first parent explicitly specified as mainline for
+                *  non-merge commit
+                */
+               return error(_("commit %s does not have parent %d"),
+                            oid_to_hex(&commit->object.oid), opts->mainline);
        else
                parent = commit->parents->item;
 
@@ -1781,7 +1810,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
             (!parent && unborn))) {
                if (is_rebase_i(opts))
                        write_author_script(msg.message);
-               res = fast_forward_to(&commit->object.oid, &head, unborn,
+               res = fast_forward_to(r, &commit->object.oid, &head, unborn,
                        opts);
                if (res || command != TODO_REWORD)
                        goto leave;
@@ -1845,7 +1874,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
        if (command == TODO_REWORD)
                flags |= EDIT_MSG | VERIFY_MSG;
        else if (is_fixup(command)) {
-               if (update_squash_messages(command, commit, opts))
+               if (update_squash_messages(r, command, commit, opts))
                        return -1;
                flags |= AMEND_MSG;
                if (!final_fixup)
@@ -1854,12 +1883,12 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                        flags |= CLEANUP_MSG;
                        msg_file = rebase_path_fixup_msg();
                } else {
-                       const char *dest = git_path_squash_msg(the_repository);
+                       const char *dest = git_path_squash_msg(r);
                        unlink(dest);
                        if (copy_file(dest, rebase_path_squash_msg(), 0666))
                                return error(_("could not rename '%s' to '%s'"),
                                             rebase_path_squash_msg(), dest);
-                       unlink(git_path_merge_msg(the_repository));
+                       unlink(git_path_merge_msg(r));
                        msg_file = dest;
                        flags |= EDIT_MSG;
                }
@@ -1871,23 +1900,23 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
        if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
                res = -1;
        else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
-               res = do_recursive_merge(base, next, base_label, next_label,
+               res = do_recursive_merge(r, base, next, base_label, next_label,
                                         &head, &msgbuf, opts);
                if (res < 0)
                        goto leave;
 
                res |= write_message(msgbuf.buf, msgbuf.len,
-                                    git_path_merge_msg(the_repository), 0);
+                                    git_path_merge_msg(r), 0);
        } else {
                struct commit_list *common = NULL;
                struct commit_list *remotes = NULL;
 
                res = write_message(msgbuf.buf, msgbuf.len,
-                                   git_path_merge_msg(the_repository), 0);
+                                   git_path_merge_msg(r), 0);
 
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
-               res |= try_merge_command(the_repository, opts->strategy,
+               res |= try_merge_command(r, opts->strategy,
                                         opts->xopts_nr, (const char **)opts->xopts,
                                        common, oid_to_hex(&head), remotes);
                free_commit_list(common);
@@ -1915,12 +1944,12 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                      ? _("could not revert %s... %s")
                      : _("could not apply %s... %s"),
                      short_commit_name(commit), msg.subject);
-               print_advice(res == 1, opts);
-               repo_rerere(the_repository, opts->allow_rerere_auto);
+               print_advice(r, res == 1, opts);
+               repo_rerere(r, opts->allow_rerere_auto);
                goto leave;
        }
 
-       allow = allow_empty(opts, commit);
+       allow = allow_empty(r, opts, commit);
        if (allow < 0) {
                res = allow;
                goto leave;
@@ -1929,7 +1958,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
        if (!opts->no_commit) {
 fast_forward_edit:
                if (author || command == TODO_REVERT || (flags & AMEND_MSG))
-                       res = do_commit(msg_file, author, opts, flags);
+                       res = do_commit(r, msg_file, author, opts, flags);
                else
                        res = error(_("unable to parse commit author"));
        }
@@ -1965,18 +1994,19 @@ static int prepare_revs(struct replay_opts *opts)
        return 0;
 }
 
-static int read_and_refresh_cache(struct replay_opts *opts)
+static int read_and_refresh_cache(struct repository *r,
+                                 struct replay_opts *opts)
 {
        struct lock_file index_lock = LOCK_INIT;
        int index_fd = hold_locked_index(&index_lock, 0);
-       if (read_index(&the_index) < 0) {
+       if (read_index(r->index) < 0) {
                rollback_lock_file(&index_lock);
                return error(_("git %s: failed to read the index"),
                        _(action_name(opts)));
        }
-       refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
+       refresh_index(r->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
        if (index_fd >= 0) {
-               if (write_locked_index(&the_index, &index_lock,
+               if (write_locked_index(r->index, &index_lock,
                                       COMMIT_LOCK | SKIP_IF_UNCHANGED)) {
                        return error(_("git %s: failed to refresh the index"),
                                _(action_name(opts)));
@@ -2021,7 +2051,8 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list)
        return todo_list->items + todo_list->nr++;
 }
 
-static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
+static int parse_insn_line(struct repository *r, struct todo_item *item,
+                          const char *bol, char *eol)
 {
        struct object_id commit_oid;
        char *end_of_object_name;
@@ -2106,11 +2137,12 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
        if (status < 0)
                return -1;
 
-       item->commit = lookup_commit_reference(the_repository, &commit_oid);
+       item->commit = lookup_commit_reference(r, &commit_oid);
        return !item->commit;
 }
 
-static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
+static int parse_insn_buffer(struct repository *r, char *buf,
+                            struct todo_list *todo_list)
 {
        struct todo_item *item;
        char *p = buf, *next_p;
@@ -2126,7 +2158,7 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
 
                item = append_new_todo(todo_list);
                item->offset_in_buf = p - todo_list->buf.buf;
-               if (parse_insn_line(item, p, eol)) {
+               if (parse_insn_line(r, item, p, eol)) {
                        res = error(_("invalid line %d: %.*s"),
                                i, (int)(eol - p), p);
                        item->command = TODO_NOOP;
@@ -2187,8 +2219,9 @@ static ssize_t strbuf_read_file_or_whine(struct strbuf *sb, const char *path)
        return len;
 }
 
-static int read_populate_todo(struct todo_list *todo_list,
-                       struct replay_opts *opts)
+static int read_populate_todo(struct repository *r,
+                             struct todo_list *todo_list,
+                             struct replay_opts *opts)
 {
        struct stat st;
        const char *todo_file = get_todo_path(opts);
@@ -2203,7 +2236,7 @@ static int read_populate_todo(struct todo_list *todo_list,
                return error(_("could not stat '%s'"), todo_file);
        fill_stat_data(&todo_list->stat, &st);
 
-       res = parse_insn_buffer(todo_list->buf.buf, todo_list);
+       res = parse_insn_buffer(r, todo_list->buf.buf, todo_list);
        if (res) {
                if (is_rebase_i(opts))
                        return error(_("please fix this using "
@@ -2234,7 +2267,7 @@ static int read_populate_todo(struct todo_list *todo_list,
                FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
 
                if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
-                               !parse_insn_buffer(done.buf.buf, &done))
+                   !parse_insn_buffer(r, done.buf.buf, &done))
                        todo_list->done_nr = count_commands(&done);
                else
                        todo_list->done_nr = 0;
@@ -2357,11 +2390,17 @@ static int read_populate_opts(struct replay_opts *opts)
                if (file_exists(rebase_path_verbose()))
                        opts->verbose = 1;
 
+               if (file_exists(rebase_path_quiet()))
+                       opts->quiet = 1;
+
                if (file_exists(rebase_path_signoff())) {
                        opts->allow_ff = 0;
                        opts->signoff = 1;
                }
 
+               if (file_exists(rebase_path_reschedule_failed_exec()))
+                       opts->reschedule_failed_exec = 1;
+
                read_strategy_opts(opts, &buf);
                strbuf_release(&buf);
 
@@ -2424,9 +2463,6 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
 
        if (quiet)
                write_file(rebase_path_quiet(), "%s\n", quiet);
-       else
-               write_file(rebase_path_quiet(), "\n");
-
        if (opts->verbose)
                write_file(rebase_path_verbose(), "%s", "");
        if (opts->strategy)
@@ -2443,6 +2479,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
                write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
        if (opts->signoff)
                write_file(rebase_path_signoff(), "--signoff\n");
+       if (opts->reschedule_failed_exec)
+               write_file(rebase_path_reschedule_failed_exec(), "%s", "");
 
        return 0;
 }
@@ -2551,12 +2589,12 @@ static int reset_for_rollback(const struct object_id *oid)
        return run_command_v_opt(argv, RUN_GIT_CMD);
 }
 
-static int rollback_single_pick(void)
+static int rollback_single_pick(struct repository *r)
 {
        struct object_id head_oid;
 
-       if (!file_exists(git_path_cherry_pick_head(the_repository)) &&
-           !file_exists(git_path_revert_head(the_repository)))
+       if (!file_exists(git_path_cherry_pick_head(r)) &&
+           !file_exists(git_path_revert_head(r)))
                return error(_("no cherry-pick or revert in progress"));
        if (read_ref_full("HEAD", 0, &head_oid, NULL))
                return error(_("cannot resolve HEAD"));
@@ -2565,7 +2603,7 @@ static int rollback_single_pick(void)
        return reset_for_rollback(&head_oid);
 }
 
-int sequencer_rollback(struct replay_opts *opts)
+int sequencer_rollback(struct repository *r, struct replay_opts *opts)
 {
        FILE *f;
        struct object_id oid;
@@ -2579,7 +2617,7 @@ int sequencer_rollback(struct replay_opts *opts)
                 * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
                 * a single-cherry-pick in progress, abort that.
                 */
-               return rollback_single_pick();
+               return rollback_single_pick(r);
        }
        if (!f)
                return error_errno(_("cannot open '%s'"), git_path_head_file());
@@ -2694,7 +2732,9 @@ static int save_opts(struct replay_opts *opts)
        return res;
 }
 
-static int make_patch(struct commit *commit, struct replay_opts *opts)
+static int make_patch(struct repository *r,
+                     struct commit *commit,
+                     struct replay_opts *opts)
 {
        struct strbuf buf = STRBUF_INIT;
        struct rev_info log_tree_opt;
@@ -2710,7 +2750,7 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)
 
        strbuf_addf(&buf, "%s/patch", get_dir(opts));
        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
-       repo_init_revisions(the_repository, &log_tree_opt, NULL);
+       repo_init_revisions(r, &log_tree_opt, NULL);
        log_tree_opt.abbrev = 0;
        log_tree_opt.diff = 1;
        log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;
@@ -2750,17 +2790,19 @@ static int intend_to_amend(void)
        return write_message(p, strlen(p), rebase_path_amend(), 1);
 }
 
-static int error_with_patch(struct commit *commit,
-       const char *subject, int subject_len,
-       struct replay_opts *opts, int exit_code, int to_amend)
+static int error_with_patch(struct repository *r,
+                           struct commit *commit,
+                           const char *subject, int subject_len,
+                           struct replay_opts *opts,
+                           int exit_code, int to_amend)
 {
        if (commit) {
-               if (make_patch(commit, opts))
+               if (make_patch(r, commit, opts))
                        return -1;
        } else if (copy_file(rebase_path_message(),
-                            git_path_merge_msg(the_repository), 0666))
+                            git_path_merge_msg(r), 0666))
                return error(_("unable to copy '%s' to '%s'"),
-                            git_path_merge_msg(the_repository), rebase_path_message());
+                            git_path_merge_msg(r), rebase_path_message());
 
        if (to_amend) {
                if (intend_to_amend())
@@ -2791,21 +2833,24 @@ static int error_with_patch(struct commit *commit,
        return exit_code;
 }
 
-static int error_failed_squash(struct commit *commit,
-       struct replay_opts *opts, int subject_len, const char *subject)
+static int error_failed_squash(struct repository *r,
+                              struct commit *commit,
+                              struct replay_opts *opts,
+                              int subject_len,
+                              const char *subject)
 {
        if (copy_file(rebase_path_message(), rebase_path_squash_msg(), 0666))
                return error(_("could not copy '%s' to '%s'"),
                        rebase_path_squash_msg(), rebase_path_message());
-       unlink(git_path_merge_msg(the_repository));
-       if (copy_file(git_path_merge_msg(the_repository), rebase_path_message(), 0666))
+       unlink(git_path_merge_msg(r));
+       if (copy_file(git_path_merge_msg(r), rebase_path_message(), 0666))
                return error(_("could not copy '%s' to '%s'"),
                             rebase_path_message(),
-                            git_path_merge_msg(the_repository));
-       return error_with_patch(commit, subject, subject_len, opts, 1, 0);
+                            git_path_merge_msg(r));
+       return error_with_patch(r, commit, subject, subject_len, opts, 1, 0);
 }
 
-static int do_exec(const char *command_line)
+static int do_exec(struct repository *r, const char *command_line)
 {
        struct argv_array child_env = ARGV_ARRAY_INIT;
        const char *child_argv[] = { NULL, NULL };
@@ -2820,10 +2865,10 @@ static int do_exec(const char *command_line)
                                          child_env.argv);
 
        /* force re-reading of the cache */
-       if (discard_cache() < 0 || read_cache() < 0)
+       if (discard_index(r->index) < 0 || read_index(r->index) < 0)
                return error(_("could not read index"));
 
-       dirty = require_clean_work_tree("rebase", NULL, 1, 1);
+       dirty = require_clean_work_tree(r, "rebase", NULL, 1, 1);
 
        if (status) {
                warning(_("execution failed: %s\n%s"
@@ -2889,9 +2934,9 @@ static int safe_append(const char *filename, const char *fmt, ...)
        return 0;
 }
 
-static int do_label(const char *name, int len)
+static int do_label(struct repository *r, const char *name, int len)
 {
-       struct ref_store *refs = get_main_ref_store(the_repository);
+       struct ref_store *refs = get_main_ref_store(r);
        struct ref_transaction *transaction;
        struct strbuf ref_name = STRBUF_INIT, err = STRBUF_INIT;
        struct strbuf msg = STRBUF_INIT;
@@ -2932,7 +2977,9 @@ static int do_label(const char *name, int len)
 static const char *reflog_message(struct replay_opts *opts,
        const char *sub_action, const char *fmt, ...);
 
-static int do_reset(const char *name, int len, struct replay_opts *opts)
+static int do_reset(struct repository *r,
+                   const char *name, int len,
+                   struct replay_opts *opts)
 {
        struct strbuf ref_name = STRBUF_INIT;
        struct object_id oid;
@@ -2981,13 +3028,13 @@ static int do_reset(const char *name, int len, struct replay_opts *opts)
        memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
        setup_unpack_trees_porcelain(&unpack_tree_opts, "reset");
        unpack_tree_opts.head_idx = 1;
-       unpack_tree_opts.src_index = &the_index;
-       unpack_tree_opts.dst_index = &the_index;
+       unpack_tree_opts.src_index = r->index;
+       unpack_tree_opts.dst_index = r->index;
        unpack_tree_opts.fn = oneway_merge;
        unpack_tree_opts.merge = 1;
        unpack_tree_opts.update = 1;
 
-       if (read_cache_unmerged()) {
+       if (read_index_unmerged(r->index)) {
                rollback_lock_file(&lock);
                strbuf_release(&ref_name);
                return error_resolve_conflict(_(action_name(opts)));
@@ -3009,9 +3056,9 @@ static int do_reset(const char *name, int len, struct replay_opts *opts)
        }
 
        tree = parse_tree_indirect(&oid);
-       prime_cache_tree(&the_index, tree);
+       prime_cache_tree(r, r->index, tree);
 
-       if (write_locked_index(&the_index, &lock, COMMIT_LOCK) < 0)
+       if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0)
                ret = error(_("could not write index"));
        free((void *)desc.buffer);
 
@@ -3044,7 +3091,9 @@ static struct commit *lookup_label(const char *label, int len,
        return commit;
 }
 
-static int do_merge(struct commit *commit, const char *arg, int arg_len,
+static int do_merge(struct repository *r,
+                   struct commit *commit,
+                   const char *arg, int arg_len,
                    int flags, struct replay_opts *opts)
 {
        int run_commit_flags = (flags & TODO_EDIT_MERGE_MSG) ?
@@ -3111,7 +3160,7 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                        ret = error(_("octopus merge cannot be executed on "
                                      "top of a [new root]"));
                else
-                       ret = fast_forward_to(&to_merge->item->object.oid,
+                       ret = fast_forward_to(r, &to_merge->item->object.oid,
                                              &head_commit->object.oid, 0,
                                              opts);
                goto leave_merge;
@@ -3130,11 +3179,11 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                write_author_script(message);
                find_commit_subject(message, &body);
                len = strlen(body);
-               ret = write_message(body, len, git_path_merge_msg(the_repository), 0);
+               ret = write_message(body, len, git_path_merge_msg(r), 0);
                unuse_commit_buffer(commit, message);
                if (ret) {
                        error_errno(_("could not write '%s'"),
-                                   git_path_merge_msg(the_repository));
+                                   git_path_merge_msg(r));
                        goto leave_merge;
                }
        } else {
@@ -3156,11 +3205,11 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                        len = buf.len;
                }
 
-               ret = write_message(p, len, git_path_merge_msg(the_repository), 0);
+               ret = write_message(p, len, git_path_merge_msg(r), 0);
                strbuf_release(&buf);
                if (ret) {
                        error_errno(_("could not write '%s'"),
-                                   git_path_merge_msg(the_repository));
+                                   git_path_merge_msg(r));
                        goto leave_merge;
                }
        }
@@ -3196,7 +3245,7 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
 
        if (can_fast_forward) {
                rollback_lock_file(&lock);
-               ret = fast_forward_to(&commit->object.oid,
+               ret = fast_forward_to(r, &commit->object.oid,
                                      &head_commit->object.oid, 0, opts);
                goto leave_merge;
        }
@@ -3221,7 +3270,7 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                argv_array_push(&cmd.args, "--no-log");
                argv_array_push(&cmd.args, "--no-stat");
                argv_array_push(&cmd.args, "-F");
-               argv_array_push(&cmd.args, git_path_merge_msg(the_repository));
+               argv_array_push(&cmd.args, git_path_merge_msg(r));
                if (opts->gpg_sign)
                        argv_array_push(&cmd.args, opts->gpg_sign);
 
@@ -3231,14 +3280,15 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                                        oid_to_hex(&j->item->object.oid));
 
                strbuf_release(&ref_name);
-               unlink(git_path_cherry_pick_head(the_repository));
+               unlink(git_path_cherry_pick_head(r));
                rollback_lock_file(&lock);
 
                rollback_lock_file(&lock);
                ret = run_command(&cmd);
 
                /* force re-reading of the cache */
-               if (!ret && (discard_cache() < 0 || read_cache() < 0))
+               if (!ret && (discard_index(r->index) < 0 ||
+                            read_index(r->index) < 0))
                        ret = error(_("could not read index"));
                goto leave_merge;
        }
@@ -3253,14 +3303,14 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
        }
 
        write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ,
-                     git_path_merge_head(the_repository), 0);
-       write_message("no-ff", 5, git_path_merge_mode(the_repository), 0);
+                     git_path_merge_head(r), 0);
+       write_message("no-ff", 5, git_path_merge_mode(r), 0);
 
        for (j = bases; j; j = j->next)
                commit_list_insert(j->item, &reversed);
        free_commit_list(bases);
 
-       read_cache();
+       read_index(r->index);
        init_merge_options(&o);
        o.branch1 = "HEAD";
        o.branch2 = ref_name.buf;
@@ -3285,23 +3335,23 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
         */
        ret = !ret;
 
-       if (active_cache_changed &&
-           write_locked_index(&the_index, &lock, COMMIT_LOCK)) {
+       if (r->index->cache_changed &&
+           write_locked_index(r->index, &lock, COMMIT_LOCK)) {
                ret = error(_("merge: Unable to write new index file"));
                goto leave_merge;
        }
 
        rollback_lock_file(&lock);
        if (ret)
-               repo_rerere(the_repository, opts->allow_rerere_auto);
+               repo_rerere(r, opts->allow_rerere_auto);
        else
                /*
                 * In case of problems, we now want to return a positive
                 * value (a negative one would indicate that the `merge`
                 * command needs to be rescheduled).
                 */
-               ret = !!run_git_commit(git_path_merge_msg(the_repository), opts,
-                                    run_commit_flags);
+               ret = !!run_git_commit(r, git_path_merge_msg(r), opts,
+                                      run_commit_flags);
 
 leave_merge:
        strbuf_release(&ref_name);
@@ -3449,14 +3499,14 @@ static int checkout_onto(struct replay_opts *opts,
        return update_ref(NULL, "ORIG_HEAD", &oid, NULL, 0, UPDATE_REFS_MSG_ON_ERR);
 }
 
-static int stopped_at_head(void)
+static int stopped_at_head(struct repository *r)
 {
        struct object_id head;
        struct commit *commit;
        struct commit_message message;
 
        if (get_oid("HEAD", &head) ||
-           !(commit = lookup_commit(the_repository, &head)) ||
+           !(commit = lookup_commit(r, &head)) ||
            parse_commit(commit) || get_message(commit, &message))
                fprintf(stderr, _("Stopped at HEAD\n"));
        else {
@@ -3478,7 +3528,9 @@ N_("Could not execute the todo command\n"
 "    git rebase --edit-todo\n"
 "    git rebase --continue\n");
 
-static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
+static int pick_commits(struct repository *r,
+                       struct todo_list *todo_list,
+                       struct replay_opts *opts)
 {
        int res = 0, reschedule = 0;
 
@@ -3486,7 +3538,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
        if (opts->allow_ff)
                assert(!(opts->signoff || opts->no_commit ||
                                opts->record_origin || opts->edit));
-       if (read_and_refresh_cache(opts))
+       if (read_and_refresh_cache(r, opts))
                return -1;
 
        while (todo_list->current < todo_list->nr) {
@@ -3503,10 +3555,11 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                        fprintf(f, "%d\n", todo_list->done_nr);
                                        fclose(f);
                                }
-                               fprintf(stderr, "Rebasing (%d/%d)%s",
-                                       todo_list->done_nr,
-                                       todo_list->total_nr,
-                                       opts->verbose ? "\n" : "\r");
+                               if (!opts->quiet)
+                                       fprintf(stderr, "Rebasing (%d/%d)%s",
+                                               todo_list->done_nr,
+                                               todo_list->total_nr,
+                                               opts->verbose ? "\n" : "\r");
                        }
                        unlink(rebase_path_message());
                        unlink(rebase_path_author_script());
@@ -3516,14 +3569,14 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 
                        if (item->command == TODO_BREAK)
-                               return stopped_at_head();
+                               return stopped_at_head(r);
                }
                if (item->command <= TODO_SQUASH) {
                        if (is_rebase_i(opts))
                                setenv("GIT_REFLOG_ACTION", reflog_message(opts,
                                        command_to_string(item->command), NULL),
                                        1);
-                       res = do_pick_commit(item->command, item->commit,
+                       res = do_pick_commit(r, item->command, item->commit,
                                        opts, is_final_fixup(todo_list));
                        if (is_rebase_i(opts) && res < 0) {
                                /* Reschedule */
@@ -3543,7 +3596,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                                _("Stopped at %s...  %.*s\n"),
                                                short_commit_name(commit),
                                                item->arg_len, item->arg);
-                               return error_with_patch(commit,
+                               return error_with_patch(r, commit,
                                        item->arg, item->arg_len, opts, res,
                                        !res);
                        }
@@ -3553,7 +3606,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        if (res && is_fixup(item->command)) {
                                if (res == 1)
                                        intend_to_amend();
-                               return error_failed_squash(item->commit, opts,
+                               return error_failed_squash(r, item->commit, opts,
                                        item->arg_len, item->arg);
                        } else if (res && is_rebase_i(opts) && item->commit) {
                                int to_amend = 0;
@@ -3572,7 +3625,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                      oideq(&opts->squash_onto, &oid))))
                                        to_amend = 1;
 
-                               return res | error_with_patch(item->commit,
+                               return res | error_with_patch(r, item->commit,
                                                item->arg, item->arg_len, opts,
                                                res, to_amend);
                        }
@@ -3582,30 +3635,31 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        struct stat st;
 
                        *end_of_arg = '\0';
-                       res = do_exec(item->arg);
+                       res = do_exec(r, item->arg);
                        *end_of_arg = saved;
 
                        /* Reread the todo file if it has changed. */
-                       if (res)
-                               ; /* fall through */
-                       else if (stat(get_todo_path(opts), &st))
+                       if (res) {
+                               if (opts->reschedule_failed_exec)
+                                       reschedule = 1;
+                       } else if (stat(get_todo_path(opts), &st))
                                res = error_errno(_("could not stat '%s'"),
                                                  get_todo_path(opts));
                        else if (match_stat_data(&todo_list->stat, &st)) {
                                todo_list_release(todo_list);
-                               if (read_populate_todo(todo_list, opts))
+                               if (read_populate_todo(r, todo_list, opts))
                                        res = -1; /* message was printed */
                                /* `current` will be incremented below */
                                todo_list->current = -1;
                        }
                } else if (item->command == TODO_LABEL) {
-                       if ((res = do_label(item->arg, item->arg_len)))
+                       if ((res = do_label(r, item->arg, item->arg_len)))
                                reschedule = 1;
                } else if (item->command == TODO_RESET) {
-                       if ((res = do_reset(item->arg, item->arg_len, opts)))
+                       if ((res = do_reset(r, item->arg, item->arg_len, opts)))
                                reschedule = 1;
                } else if (item->command == TODO_MERGE) {
-                       if ((res = do_merge(item->commit,
+                       if ((res = do_merge(r, item->commit,
                                            item->arg, item->arg_len,
                                            item->flags, opts)) < 0)
                                reschedule = 1;
@@ -3614,7 +3668,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                                    peek_command(todo_list, 1));
                        if (res > 0)
                                /* failed with merge conflicts */
-                               return error_with_patch(item->commit,
+                               return error_with_patch(r, item->commit,
                                                        item->arg,
                                                        item->arg_len, opts,
                                                        res, 0);
@@ -3630,7 +3684,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        if (save_todo(todo_list, opts))
                                return -1;
                        if (item->commit)
-                               return error_with_patch(item->commit,
+                               return error_with_patch(r,
+                                                       item->commit,
                                                        item->arg,
                                                        item->arg_len, opts,
                                                        res, 0);
@@ -3695,7 +3750,7 @@ cleanup_head_ref:
                        struct object_id orig, head;
 
                        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
-                       repo_init_revisions(the_repository, &log_tree_opt, NULL);
+                       repo_init_revisions(r, &log_tree_opt, NULL);
                        log_tree_opt.diff = 1;
                        log_tree_opt.diffopt.output_format =
                                DIFF_FORMAT_DIFFSTAT;
@@ -3738,8 +3793,10 @@ cleanup_head_ref:
                }
                apply_autostash(opts);
 
-               fprintf(stderr, "Successfully rebased and updated %s.\n",
-                       head_ref.buf);
+               if (!opts->quiet)
+                       fprintf(stderr,
+                               "Successfully rebased and updated %s.\n",
+                               head_ref.buf);
 
                strbuf_release(&buf);
                strbuf_release(&head_ref);
@@ -3752,26 +3809,27 @@ cleanup_head_ref:
        return sequencer_remove_state(opts);
 }
 
-static int continue_single_pick(void)
+static int continue_single_pick(struct repository *r)
 {
        const char *argv[] = { "commit", NULL };
 
-       if (!file_exists(git_path_cherry_pick_head(the_repository)) &&
-           !file_exists(git_path_revert_head(the_repository)))
+       if (!file_exists(git_path_cherry_pick_head(r)) &&
+           !file_exists(git_path_revert_head(r)))
                return error(_("no cherry-pick or revert in progress"));
        return run_command_v_opt(argv, RUN_GIT_CMD);
 }
 
-static int commit_staged_changes(struct replay_opts *opts,
+static int commit_staged_changes(struct repository *r,
+                                struct replay_opts *opts,
                                 struct todo_list *todo_list)
 {
        unsigned int flags = ALLOW_EMPTY | EDIT_MSG;
        unsigned int final_fixup = 0, is_clean;
 
-       if (has_unstaged_changes(1))
+       if (has_unstaged_changes(r, 1))
                return error(_("cannot rebase: You have unstaged changes."));
 
-       is_clean = !has_uncommitted_changes(0);
+       is_clean = !has_uncommitted_changes(r, 0);
 
        if (file_exists(rebase_path_amend())) {
                struct strbuf rev = STRBUF_INIT;
@@ -3855,7 +3913,7 @@ static int commit_staged_changes(struct replay_opts *opts,
                                struct commit *commit;
                                const char *path = rebase_path_squash_msg();
 
-                               if (parse_head(&commit) ||
+                               if (parse_head(r, &commit) ||
                                    !(p = get_commit_buffer(commit, NULL)) ||
                                    write_message(p, strlen(p), path, 0)) {
                                        unuse_commit_buffer(commit, p);
@@ -3871,7 +3929,7 @@ static int commit_staged_changes(struct replay_opts *opts,
        }
 
        if (is_clean) {
-               const char *cherry_pick_head = git_path_cherry_pick_head(the_repository);
+               const char *cherry_pick_head = git_path_cherry_pick_head(r);
 
                if (file_exists(cherry_pick_head) && unlink(cherry_pick_head))
                        return error(_("could not remove CHERRY_PICK_HEAD"));
@@ -3879,7 +3937,7 @@ static int commit_staged_changes(struct replay_opts *opts,
                        return 0;
        }
 
-       if (run_git_commit(final_fixup ? NULL : rebase_path_message(),
+       if (run_git_commit(r, final_fixup ? NULL : rebase_path_message(),
                           opts, flags))
                return error(_("could not commit staged changes."));
        unlink(rebase_path_amend());
@@ -3900,36 +3958,36 @@ static int commit_staged_changes(struct replay_opts *opts,
        return 0;
 }
 
-int sequencer_continue(struct replay_opts *opts)
+int sequencer_continue(struct repository *r, struct replay_opts *opts)
 {
        struct todo_list todo_list = TODO_LIST_INIT;
        int res;
 
-       if (read_and_refresh_cache(opts))
+       if (read_and_refresh_cache(r, opts))
                return -1;
 
        if (read_populate_opts(opts))
                return -1;
        if (is_rebase_i(opts)) {
-               if ((res = read_populate_todo(&todo_list, opts)))
+               if ((res = read_populate_todo(r, &todo_list, opts)))
                        goto release_todo_list;
-               if (commit_staged_changes(opts, &todo_list))
+               if (commit_staged_changes(r, opts, &todo_list))
                        return -1;
        } else if (!file_exists(get_todo_path(opts)))
-               return continue_single_pick();
-       else if ((res = read_populate_todo(&todo_list, opts)))
+               return continue_single_pick(r);
+       else if ((res = read_populate_todo(r, &todo_list, opts)))
                goto release_todo_list;
 
        if (!is_rebase_i(opts)) {
                /* Verify that the conflict has been resolved */
-               if (file_exists(git_path_cherry_pick_head(the_repository)) ||
-                   file_exists(git_path_revert_head(the_repository))) {
-                       res = continue_single_pick();
+               if (file_exists(git_path_cherry_pick_head(r)) ||
+                   file_exists(git_path_revert_head(r))) {
+                       res = continue_single_pick(r);
                        if (res)
                                goto release_todo_list;
                }
-               if (index_differs_from("HEAD", NULL, 0)) {
-                       res = error_dirty_index(opts);
+               if (index_differs_from(r, "HEAD", NULL, 0)) {
+                       res = error_dirty_index(r->index, opts);
                        goto release_todo_list;
                }
                todo_list.current++;
@@ -3943,27 +4001,30 @@ int sequencer_continue(struct replay_opts *opts)
                strbuf_release(&buf);
        }
 
-       res = pick_commits(&todo_list, opts);
+       res = pick_commits(r, &todo_list, opts);
 release_todo_list:
        todo_list_release(&todo_list);
        return res;
 }
 
-static int single_pick(struct commit *cmit, struct replay_opts *opts)
+static int single_pick(struct repository *r,
+                      struct commit *cmit,
+                      struct replay_opts *opts)
 {
        setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
-       return do_pick_commit(opts->action == REPLAY_PICK ?
+       return do_pick_commit(r, opts->action == REPLAY_PICK ?
                TODO_PICK : TODO_REVERT, cmit, opts, 0);
 }
 
-int sequencer_pick_revisions(struct replay_opts *opts)
+int sequencer_pick_revisions(struct repository *r,
+                            struct replay_opts *opts)
 {
        struct todo_list todo_list = TODO_LIST_INIT;
        struct object_id oid;
        int i, res;
 
        assert(opts->revs);
-       if (read_and_refresh_cache(opts))
+       if (read_and_refresh_cache(r, opts))
                return -1;
 
        for (i = 0; i < opts->revs->pending.nr; i++) {
@@ -3975,8 +4036,8 @@ int sequencer_pick_revisions(struct replay_opts *opts)
                        continue;
 
                if (!get_oid(name, &oid)) {
-                       if (!lookup_commit_reference_gently(the_repository, &oid, 1)) {
-                               enum object_type type = oid_object_info(the_repository,
+                       if (!lookup_commit_reference_gently(r, &oid, 1)) {
+                               enum object_type type = oid_object_info(r,
                                                                        &oid,
                                                                        NULL);
                                return error(_("%s: can't cherry-pick a %s"),
@@ -4005,7 +4066,7 @@ int sequencer_pick_revisions(struct replay_opts *opts)
                        return error(_("empty commit set passed"));
                if (get_revision(opts->revs))
                        BUG("unexpected extra commit from walk");
-               return single_pick(cmit, opts);
+               return single_pick(r, cmit, opts);
        }
 
        /*
@@ -4024,7 +4085,7 @@ int sequencer_pick_revisions(struct replay_opts *opts)
        if (save_opts(opts))
                return -1;
        update_abort_safety_file();
-       res = pick_commits(&todo_list, opts);
+       res = pick_commits(r, &todo_list, opts);
        todo_list_release(&todo_list);
        return res;
 }
@@ -4428,7 +4489,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
        return 0;
 }
 
-int sequencer_make_script(FILE *out, int argc, const char **argv,
+int sequencer_make_script(struct repository *r, FILE *out,
+                         int argc, const char **argv,
                          unsigned flags)
 {
        char *format = NULL;
@@ -4440,7 +4502,7 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,
        const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
        int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
 
-       repo_init_revisions(the_repository, &revs, NULL);
+       repo_init_revisions(r, &revs, NULL);
        revs.verbose_header = 1;
        if (!rebase_merges)
                revs.max_parents = 1;
@@ -4493,7 +4555,8 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,
  * Add commands after pick and (series of) squash/fixup commands
  * in the todo list.
  */
-int sequencer_add_exec_commands(const char *commands)
+int sequencer_add_exec_commands(struct repository *r,
+                               const char *commands)
 {
        const char *todo_file = rebase_path_todo();
        struct todo_list todo_list = TODO_LIST_INIT;
@@ -4504,7 +4567,7 @@ int sequencer_add_exec_commands(const char *commands)
        if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
                return error(_("could not read '%s'."), todo_file);
 
-       if (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {
+       if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
                todo_list_release(&todo_list);
                return error(_("unusable todo list: '%s'"), todo_file);
        }
@@ -4549,7 +4612,7 @@ int sequencer_add_exec_commands(const char *commands)
        return i;
 }
 
-int transform_todos(unsigned flags)
+int transform_todos(struct repository *r, unsigned flags)
 {
        const char *todo_file = rebase_path_todo();
        struct todo_list todo_list = TODO_LIST_INIT;
@@ -4560,7 +4623,7 @@ int transform_todos(unsigned flags)
        if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
                return error(_("could not read '%s'."), todo_file);
 
-       if (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {
+       if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
                todo_list_release(&todo_list);
                return error(_("unusable todo list: '%s'"), todo_file);
        }
@@ -4629,7 +4692,7 @@ define_commit_slab(commit_seen, unsigned char);
  * Check if there is an unrecognized command or a
  * bad SHA-1 in a command.
  */
-int check_todo_list(void)
+int check_todo_list(struct repository *r)
 {
        enum missing_commit_check_level check_level = get_missing_commit_check_level();
        struct strbuf todo_file = STRBUF_INIT;
@@ -4646,7 +4709,7 @@ int check_todo_list(void)
                goto leave_check;
        }
        advise_to_edit_todo = res =
-               parse_insn_buffer(todo_list.buf.buf, &todo_list);
+               parse_insn_buffer(r, todo_list.buf.buf, &todo_list);
 
        if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
                goto leave_check;
@@ -4665,7 +4728,7 @@ int check_todo_list(void)
                goto leave_check;
        }
        strbuf_release(&todo_file);
-       res = !!parse_insn_buffer(todo_list.buf.buf, &todo_list);
+       res = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list);
 
        /* Find commits in git-rebase-todo.backup yet unseen */
        for (i = todo_list.nr - 1; i >= 0; i--) {
@@ -4729,7 +4792,7 @@ static int rewrite_file(const char *path, const char *buf, size_t len)
 }
 
 /* skip picking commits whose parents are unchanged */
-static int skip_unnecessary_picks(struct object_id *output_oid)
+static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)
 {
        const char *todo_file = rebase_path_todo();
        struct strbuf buf = STRBUF_INIT;
@@ -4747,7 +4810,7 @@ static int skip_unnecessary_picks(struct object_id *output_oid)
 
        if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
                return -1;
-       if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {
+       if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
                todo_list_release(&todo_list);
                return -1;
        }
@@ -4808,7 +4871,7 @@ static int skip_unnecessary_picks(struct object_id *output_oid)
        return 0;
 }
 
-int complete_action(struct replay_opts *opts, unsigned flags,
+int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
                    const char *shortrevisions, const char *onto_name,
                    const char *onto, const char *orig_head, const char *cmd,
                    unsigned autosquash)
@@ -4826,16 +4889,16 @@ int complete_action(struct replay_opts *opts, unsigned flags,
            write_message("noop\n", 5, todo_file, 0))
                return -1;
 
-       if (autosquash && rearrange_squash())
+       if (autosquash && rearrange_squash(r))
                return -1;
 
        if (cmd && *cmd)
-               sequencer_add_exec_commands(cmd);
+               sequencer_add_exec_commands(r, cmd);
 
        if (strbuf_read_file(buf, todo_file, 0) < 0)
                return error_errno(_("could not read '%s'."), todo_file);
 
-       if (parse_insn_buffer(buf->buf, &todo_list)) {
+       if (parse_insn_buffer(r, buf->buf, &todo_list)) {
                todo_list_release(&todo_list);
                return error(_("unusable todo list: '%s'"), todo_file);
        }
@@ -4864,7 +4927,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,
                return error(_("could not copy '%s' to '%s'."), todo_file,
                             rebase_path_todo_backup());
 
-       if (transform_todos(flags | TODO_LIST_SHORTEN_IDS))
+       if (transform_todos(r, flags | TODO_LIST_SHORTEN_IDS))
                return error(_("could not transform the todo list"));
 
        strbuf_reset(buf);
@@ -4888,24 +4951,24 @@ int complete_action(struct replay_opts *opts, unsigned flags,
 
        todo_list_release(&todo_list);
 
-       if (check_todo_list()) {
+       if (check_todo_list(r)) {
                checkout_onto(opts, onto_name, onto, orig_head);
                return -1;
        }
 
-       if (transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS)))
+       if (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS)))
                return error(_("could not transform the todo list"));
 
-       if (opts->allow_ff && skip_unnecessary_picks(&oid))
+       if (opts->allow_ff && skip_unnecessary_picks(r, &oid))
                return error(_("could not skip unnecessary pick commands"));
 
        if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
                return -1;
 
-       if (require_clean_work_tree("rebase", "", 1, 1))
+       if (require_clean_work_tree(r, "rebase", "", 1, 1))
                return -1;
 
-       return sequencer_continue(opts);
+       return sequencer_continue(r, opts);
 }
 
 struct subject2item_entry {
@@ -4932,7 +4995,7 @@ define_commit_slab(commit_todo_item, struct todo_item *);
  * message will have to be retrieved from the commit (as the oneline in the
  * script cannot be trusted) in order to normalize the autosquash arrangement.
  */
-int rearrange_squash(void)
+int rearrange_squash(struct repository *r)
 {
        const char *todo_file = rebase_path_todo();
        struct todo_list todo_list = TODO_LIST_INIT;
@@ -4943,7 +5006,7 @@ int rearrange_squash(void)
 
        if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
                return -1;
-       if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {
+       if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
                todo_list_release(&todo_list);
                return -1;
        }
index 5071a73563f1cdfc35060603d1fef4e085497798..53b86797188e8bef7371cd922bfafdc2464c1281 100644 (file)
@@ -5,6 +5,7 @@
 #include "strbuf.h"
 
 struct commit;
+struct repository;
 
 const char *git_path_commit_editmsg(void);
 const char *git_path_seq_dir(void);
@@ -39,6 +40,8 @@ struct replay_opts {
        int allow_empty_message;
        int keep_redundant_commits;
        int verbose;
+       int quiet;
+       int reschedule_failed_exec;
 
        int mainline;
 
@@ -74,9 +77,10 @@ int write_message(const void *buf, size_t len, const char *filename,
 
 /* Call this to setup defaults before parsing command line options */
 void sequencer_init_config(struct replay_opts *opts);
-int sequencer_pick_revisions(struct replay_opts *opts);
-int sequencer_continue(struct replay_opts *opts);
-int sequencer_rollback(struct replay_opts *opts);
+int sequencer_pick_revisions(struct repository *repo,
+                            struct replay_opts *opts);
+int sequencer_continue(struct repository *repo, struct replay_opts *opts);
+int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
 int sequencer_remove_state(struct replay_opts *opts);
 
 #define TODO_LIST_KEEP_EMPTY (1U << 0)
@@ -89,18 +93,19 @@ int sequencer_remove_state(struct replay_opts *opts);
  * commits should be rebased onto the new base, this flag needs to be passed.
  */
 #define TODO_LIST_REBASE_COUSINS (1U << 4)
-int sequencer_make_script(FILE *out, int argc, const char **argv,
+int sequencer_make_script(struct repository *repo, FILE *out,
+                         int argc, const char **argv,
                          unsigned flags);
 
-int sequencer_add_exec_commands(const char *command);
-int transform_todos(unsigned flags);
+int sequencer_add_exec_commands(struct repository *r, const char *command);
+int transform_todos(struct repository *r, unsigned flags);
 enum missing_commit_check_level get_missing_commit_check_level(void);
-int check_todo_list(void);
-int complete_action(struct replay_opts *opts, unsigned flags,
+int check_todo_list(struct repository *r);
+int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
                    const char *shortrevisions, const char *onto_name,
                    const char *onto, const char *orig_head, const char *cmd,
                    unsigned autosquash);
-int rearrange_squash(void);
+int rearrange_squash(struct repository *r);
 
 extern const char sign_off_header[];
 
@@ -112,7 +117,7 @@ extern const char sign_off_header[];
  */
 void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
 
-void append_conflicts_hint(struct strbuf *msgbuf);
+void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);
 int message_is_empty(const struct strbuf *sb,
                     enum commit_msg_cleanup_mode cleanup_mode);
 int template_untouched(const struct strbuf *sb, const char *template_file,
@@ -128,7 +133,9 @@ int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
-void print_commit_summary(const char *prefix, const struct object_id *oid,
+void print_commit_summary(struct repository *repo,
+                         const char *prefix,
+                         const struct object_id *oid,
                          unsigned int flags);
 
 int read_author_script(const char *path, char **name, char **email, char **date,
diff --git a/serve.c b/serve.c
index bda085f09c8e10314c2c497dbca978fd9241e7b5..317256c1a493c4b2cc730a6212add4d1ef5696d4 100644 (file)
--- a/serve.c
+++ b/serve.c
@@ -167,7 +167,8 @@ static int process_request(void)
 
        packet_reader_init(&reader, 0, NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE |
-                          PACKET_READ_GENTLE_ON_EOF);
+                          PACKET_READ_GENTLE_ON_EOF |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
 
        /*
         * Check to see if the client closed their end before sending another
@@ -175,7 +176,7 @@ static int process_request(void)
         */
        if (packet_reader_peek(&reader) == PACKET_READ_EOF)
                return 1;
-       reader.options = PACKET_READ_CHOMP_NEWLINE;
+       reader.options &= ~PACKET_READ_GENTLE_ON_EOF;
 
        while (state != PROCESS_REQUEST_DONE) {
                switch (packet_reader_peek(&reader)) {
diff --git a/setup.c b/setup.c
index 1be5037f129646cd46d3dc048e8f58dc3bdac0b9..ca9e8a949ed869183f16c57520f19fb361100a02 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -39,7 +39,7 @@ static int abspath_part_inside_repo(char *path)
        off = offset_1st_component(path);
 
        /* check if work tree is already the prefix */
-       if (wtlen <= len && !strncmp(path, work_tree, wtlen)) {
+       if (wtlen <= len && !fspathncmp(path, work_tree, wtlen)) {
                if (path[wtlen] == '/') {
                        memmove(path, path + wtlen + 1, len - wtlen);
                        return 0;
@@ -59,7 +59,7 @@ static int abspath_part_inside_repo(char *path)
                path++;
                if (*path == '/') {
                        *path = '\0';
-                       if (strcmp(real_path(path0), work_tree) == 0) {
+                       if (fspathcmp(real_path(path0), work_tree) == 0) {
                                memmove(path0, path + 1, len - (path - path0));
                                return 0;
                        }
@@ -68,7 +68,7 @@ static int abspath_part_inside_repo(char *path)
        }
 
        /* check whole path */
-       if (strcmp(real_path(path0), work_tree) == 0) {
+       if (fspathcmp(real_path(path0), work_tree) == 0) {
                *path0 = '\0';
                return 0;
        }
@@ -831,16 +831,6 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
        return NULL;
 }
 
-static const char *setup_nongit(const char *cwd, int *nongit_ok)
-{
-       if (!nongit_ok)
-               die(_("not a git repository (or any of the parent directories): %s"), DEFAULT_GIT_DIR_ENVIRONMENT);
-       if (chdir(cwd))
-               die_errno(_("cannot come back to cwd"));
-       *nongit_ok = 1;
-       return NULL;
-}
-
 static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
 {
        struct stat buf;
@@ -1054,7 +1044,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
 {
        static struct strbuf cwd = STRBUF_INIT;
        struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT;
-       const char *prefix;
+       const char *prefix = NULL;
        struct repository_format repo_fmt;
 
        /*
@@ -1079,9 +1069,6 @@ const char *setup_git_directory_gently(int *nongit_ok)
        strbuf_addbuf(&dir, &cwd);
 
        switch (setup_git_directory_gently_1(&dir, &gitdir, 1)) {
-       case GIT_DIR_NONE:
-               prefix = NULL;
-               break;
        case GIT_DIR_EXPLICIT:
                prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok);
                break;
@@ -1097,29 +1084,51 @@ const char *setup_git_directory_gently(int *nongit_ok)
                prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_HIT_CEILING:
-               prefix = setup_nongit(cwd.buf, nongit_ok);
+               if (!nongit_ok)
+                       die(_("not a git repository (or any of the parent directories): %s"),
+                           DEFAULT_GIT_DIR_ENVIRONMENT);
+               *nongit_ok = 1;
                break;
        case GIT_DIR_HIT_MOUNT_POINT:
-               if (nongit_ok) {
-                       *nongit_ok = 1;
-                       strbuf_release(&cwd);
-                       strbuf_release(&dir);
-                       return NULL;
-               }
-               die(_("not a git repository (or any parent up to mount point %s)\n"
-                     "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
-                   dir.buf);
+               if (!nongit_ok)
+                       die(_("not a git repository (or any parent up to mount point %s)\n"
+                             "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
+                           dir.buf);
+               *nongit_ok = 1;
+               break;
+       case GIT_DIR_NONE:
+               /*
+                * As a safeguard against setup_git_directory_gently_1 returning
+                * this value, fallthrough to BUG. Otherwise it is possible to
+                * set startup_info->have_repository to 1 when we did nothing to
+                * find a repository.
+                */
        default:
                BUG("unhandled setup_git_directory_1() result");
        }
 
-       if (prefix)
-               setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
-       else
+       /*
+        * At this point, nongit_ok is stable. If it is non-NULL and points
+        * to a non-zero value, then this means that we haven't found a
+        * repository and that the caller expects startup_info to reflect
+        * this.
+        *
+        * Regardless of the state of nongit_ok, startup_info->prefix and
+        * the GIT_PREFIX environment variable must always match. For details
+        * see Documentation/config/alias.txt.
+        */
+       if (nongit_ok && *nongit_ok) {
+               startup_info->have_repository = 0;
+               startup_info->prefix = NULL;
                setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
-
-       startup_info->have_repository = !nongit_ok || !*nongit_ok;
-       startup_info->prefix = prefix;
+       } else {
+               startup_info->have_repository = 1;
+               startup_info->prefix = prefix;
+               if (prefix)
+                       setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
+               else
+                       setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
+       }
 
        /*
         * Not all paths through the setup code will call 'set_git_dir()' (which
@@ -1132,7 +1141,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
         * the user has set GIT_DIR.  It may be beneficial to disallow bogus
         * GIT_DIR values at some point in the future.
         */
-       if (startup_info->have_repository || getenv(GIT_DIR_ENVIRONMENT)) {
+       if (/* GIT_DIR_EXPLICIT, GIT_DIR_DISCOVERED, GIT_DIR_BARE */
+           startup_info->have_repository ||
+           /* GIT_DIR_EXPLICIT */
+           getenv(GIT_DIR_ENVIRONMENT)) {
                if (!the_repository->gitdir) {
                        const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
                        if (!gitdir)
index b94e0ec0f5e45fa56e1748bbb611a904ab81de67..d922e94e3fc58488402ac0095075bf5ef0e4d17b 100644 (file)
@@ -77,3 +77,20 @@ int oid_array_for_each_unique(struct oid_array *array,
        }
        return 0;
 }
+
+void oid_array_filter(struct oid_array *array,
+                     for_each_oid_fn want,
+                     void *cb_data)
+{
+       unsigned nr = array->nr, src, dst;
+       struct object_id *oids = array->oid;
+
+       for (src = dst = 0; src < nr; src++) {
+               if (want(&oids[src], cb_data)) {
+                       if (src != dst)
+                               oidcpy(&oids[dst], &oids[src]);
+                       dst++;
+               }
+       }
+       array->nr = dst;
+}
index 232bf9501729696846f514e13ce5f23c425d9d27..55d016c4bf7bf56936f034b7550996d1b7ca6473 100644 (file)
@@ -22,5 +22,8 @@ int oid_array_for_each(struct oid_array *array,
 int oid_array_for_each_unique(struct oid_array *array,
                              for_each_oid_fn fn,
                              void *data);
+void oid_array_filter(struct oid_array *array,
+                     for_each_oid_fn want,
+                     void *cbdata);
 
 #endif /* SHA1_ARRAY_H */
index 5bd11c85bc563618b59dad3ff06e5ae5296c892b..c8da9f34752c4d8f6518c8ed8c42164368e973a3 100644 (file)
 #define EMPTY_TREE_SHA1_BIN_LITERAL \
         "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
         "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
+#define EMPTY_TREE_SHA256_BIN_LITERAL \
+       "\x6e\xf1\x9b\x41\x22\x5c\x53\x69\xf1\xc1" \
+       "\x04\xd4\x5d\x8d\x85\xef\xa9\xb0\x57\xb5" \
+       "\x3b\x14\xb4\xb9\xb9\x39\xdd\x74\xde\xcc" \
+       "\x53\x21"
 
 #define EMPTY_BLOB_SHA1_BIN_LITERAL \
        "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
        "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
+#define EMPTY_BLOB_SHA256_BIN_LITERAL \
+       "\x47\x3a\x0f\x4c\x3b\xe8\xa9\x36\x81\xa2" \
+       "\x67\xe3\xb1\xe9\xa7\xdc\xda\x11\x85\x43" \
+       "\x6f\xe1\x41\xf7\x74\x91\x20\xa3\x03\x72" \
+       "\x18\x13"
 
 const unsigned char null_sha1[GIT_MAX_RAWSZ];
 const struct object_id null_oid;
@@ -53,6 +63,12 @@ static const struct object_id empty_tree_oid = {
 static const struct object_id empty_blob_oid = {
        EMPTY_BLOB_SHA1_BIN_LITERAL
 };
+static const struct object_id empty_tree_oid_sha256 = {
+       EMPTY_TREE_SHA256_BIN_LITERAL
+};
+static const struct object_id empty_blob_oid_sha256 = {
+       EMPTY_BLOB_SHA256_BIN_LITERAL
+};
 
 static void git_hash_sha1_init(git_hash_ctx *ctx)
 {
@@ -69,6 +85,22 @@ static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx)
        git_SHA1_Final(hash, &ctx->sha1);
 }
 
+
+static void git_hash_sha256_init(git_hash_ctx *ctx)
+{
+       git_SHA256_Init(&ctx->sha256);
+}
+
+static void git_hash_sha256_update(git_hash_ctx *ctx, const void *data, size_t len)
+{
+       git_SHA256_Update(&ctx->sha256, data, len);
+}
+
+static void git_hash_sha256_final(unsigned char *hash, git_hash_ctx *ctx)
+{
+       git_SHA256_Final(hash, &ctx->sha256);
+}
+
 static void git_hash_unknown_init(git_hash_ctx *ctx)
 {
        BUG("trying to init unknown hash");
@@ -90,6 +122,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
                0x00000000,
                0,
                0,
+               0,
                git_hash_unknown_init,
                git_hash_unknown_update,
                git_hash_unknown_final,
@@ -97,17 +130,31 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
                NULL,
        },
        {
-               "sha-1",
+               "sha1",
                /* "sha1", big-endian */
                0x73686131,
                GIT_SHA1_RAWSZ,
                GIT_SHA1_HEXSZ,
+               GIT_SHA1_BLKSZ,
                git_hash_sha1_init,
                git_hash_sha1_update,
                git_hash_sha1_final,
                &empty_tree_oid,
                &empty_blob_oid,
        },
+       {
+               "sha256",
+               /* "s256", big-endian */
+               0x73323536,
+               GIT_SHA256_RAWSZ,
+               GIT_SHA256_HEXSZ,
+               GIT_SHA256_BLKSZ,
+               git_hash_sha256_init,
+               git_hash_sha256_update,
+               git_hash_sha256_final,
+               &empty_tree_oid_sha256,
+               &empty_blob_oid_sha256,
+       }
 };
 
 const char *empty_tree_oid_hex(void)
@@ -122,6 +169,27 @@ const char *empty_blob_oid_hex(void)
        return oid_to_hex_r(buf, the_hash_algo->empty_blob);
 }
 
+int hash_algo_by_name(const char *name)
+{
+       int i;
+       if (!name)
+               return GIT_HASH_UNKNOWN;
+       for (i = 1; i < GIT_HASH_NALGOS; i++)
+               if (!strcmp(name, hash_algos[i].name))
+                       return i;
+       return GIT_HASH_UNKNOWN;
+}
+
+int hash_algo_by_id(uint32_t format_id)
+{
+       int i;
+       for (i = 1; i < GIT_HASH_NALGOS; i++)
+               if (format_id == hash_algos[i].format_id)
+                       return i;
+       return GIT_HASH_UNKNOWN;
+}
+
+
 /*
  * This is meant to hold a *small* number of objects that you would
  * want read_sha1_file() to be able to return, but yet you do not want
@@ -346,25 +414,21 @@ static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1)
        }
 }
 
-void sha1_file_name(struct repository *r, struct strbuf *buf, const unsigned char *sha1)
+static const char *odb_loose_path(struct object_directory *odb,
+                                 struct strbuf *buf,
+                                 const unsigned char *sha1)
 {
-       strbuf_addstr(buf, r->objects->objectdir);
+       strbuf_reset(buf);
+       strbuf_addstr(buf, odb->path);
        strbuf_addch(buf, '/');
        fill_sha1_path(buf, sha1);
+       return buf->buf;
 }
 
-struct strbuf *alt_scratch_buf(struct alternate_object_database *alt)
+const char *loose_object_path(struct repository *r, struct strbuf *buf,
+                             const unsigned char *sha1)
 {
-       strbuf_setlen(&alt->scratch, alt->base_len);
-       return &alt->scratch;
-}
-
-static const char *alt_sha1_path(struct alternate_object_database *alt,
-                                const unsigned char *sha1)
-{
-       struct strbuf *buf = alt_scratch_buf(alt);
-       fill_sha1_path(buf, sha1);
-       return buf->buf;
+       return odb_loose_path(r->objects->odb, buf, sha1);
 }
 
 /*
@@ -374,7 +438,7 @@ static int alt_odb_usable(struct raw_object_store *o,
                          struct strbuf *path,
                          const char *normalized_objdir)
 {
-       struct alternate_object_database *alt;
+       struct object_directory *odb;
 
        /* Detect cases where alternate disappeared */
        if (!is_directory(path->buf)) {
@@ -388,8 +452,8 @@ static int alt_odb_usable(struct raw_object_store *o,
         * Prevent the common mistake of listing the same
         * thing twice, or object directory itself.
         */
-       for (alt = o->alt_odb_list; alt; alt = alt->next) {
-               if (!fspathcmp(path->buf, alt->path))
+       for (odb = o->odb; odb; odb = odb->next) {
+               if (!fspathcmp(path->buf, odb->path))
                        return 0;
        }
        if (!fspathcmp(path->buf, normalized_objdir))
@@ -402,7 +466,7 @@ static int alt_odb_usable(struct raw_object_store *o,
  * Prepare alternate object database registry.
  *
  * The variable alt_odb_list points at the list of struct
- * alternate_object_database.  The elements on this list come from
+ * object_directory.  The elements on this list come from
  * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT
  * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,
  * whose contents is similar to that environment variable but can be
@@ -419,7 +483,7 @@ static void read_info_alternates(struct repository *r,
 static int link_alt_odb_entry(struct repository *r, const char *entry,
        const char *relative_base, int depth, const char *normalized_objdir)
 {
-       struct alternate_object_database *ent;
+       struct object_directory *ent;
        struct strbuf pathbuf = STRBUF_INIT;
 
        if (!is_absolute_path(entry) && relative_base) {
@@ -447,11 +511,12 @@ static int link_alt_odb_entry(struct repository *r, const char *entry,
                return -1;
        }
 
-       ent = alloc_alt_odb(pathbuf.buf);
+       ent = xcalloc(1, sizeof(*ent));
+       ent->path = xstrdup(pathbuf.buf);
 
        /* add the alternate entry */
-       *r->objects->alt_odb_tail = ent;
-       r->objects->alt_odb_tail = &(ent->next);
+       *r->objects->odb_tail = ent;
+       r->objects->odb_tail = &(ent->next);
        ent->next = NULL;
 
        /* recursively add alternates */
@@ -505,7 +570,7 @@ static void link_alt_odb_entries(struct repository *r, const char *alt,
                return;
        }
 
-       strbuf_add_absolute_path(&objdirbuf, r->objects->objectdir);
+       strbuf_add_absolute_path(&objdirbuf, r->objects->odb->path);
        if (strbuf_normalize_path(&objdirbuf) < 0)
                die(_("unable to normalize object directory: %s"),
                    objdirbuf.buf);
@@ -540,18 +605,6 @@ static void read_info_alternates(struct repository *r,
        free(path);
 }
 
-struct alternate_object_database *alloc_alt_odb(const char *dir)
-{
-       struct alternate_object_database *ent;
-
-       FLEX_ALLOC_STR(ent, path, dir);
-       strbuf_init(&ent->scratch, 0);
-       strbuf_addf(&ent->scratch, "%s/", dir);
-       ent->base_len = ent->scratch.len;
-
-       return ent;
-}
-
 void add_to_alternates_file(const char *reference)
 {
        struct lock_file lock = LOCK_INIT;
@@ -588,7 +641,7 @@ void add_to_alternates_file(const char *reference)
                fprintf_or_die(out, "%s\n", reference);
                if (commit_lock_file(&lock))
                        die_errno(_("unable to move new alternates file into place"));
-               if (the_repository->objects->alt_odb_tail)
+               if (the_repository->objects->loaded_alternates)
                        link_alt_odb_entries(the_repository, reference,
                                             '\n', NULL, 0);
        }
@@ -684,11 +737,11 @@ out:
 
 int foreach_alt_odb(alt_odb_fn fn, void *cb)
 {
-       struct alternate_object_database *ent;
+       struct object_directory *ent;
        int r = 0;
 
        prepare_alt_odb(the_repository);
-       for (ent = the_repository->objects->alt_odb_list; ent; ent = ent->next) {
+       for (ent = the_repository->objects->odb->next; ent; ent = ent->next) {
                r = fn(ent, cb);
                if (r)
                        break;
@@ -698,13 +751,13 @@ int foreach_alt_odb(alt_odb_fn fn, void *cb)
 
 void prepare_alt_odb(struct repository *r)
 {
-       if (r->objects->alt_odb_tail)
+       if (r->objects->loaded_alternates)
                return;
 
-       r->objects->alt_odb_tail = &r->objects->alt_odb_list;
        link_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);
 
-       read_info_alternates(r, r->objects->objectdir, 0);
+       read_info_alternates(r, r->objects->odb->path, 0);
+       r->objects->loaded_alternates = 1;
 }
 
 /* Returns 1 if we have successfully freshened the file, 0 otherwise. */
@@ -731,23 +784,27 @@ int check_and_freshen_file(const char *fn, int freshen)
        return 1;
 }
 
-static int check_and_freshen_local(const struct object_id *oid, int freshen)
+static int check_and_freshen_odb(struct object_directory *odb,
+                                const struct object_id *oid,
+                                int freshen)
 {
-       static struct strbuf buf = STRBUF_INIT;
-
-       strbuf_reset(&buf);
-       sha1_file_name(the_repository, &buf, oid->hash);
+       static struct strbuf path = STRBUF_INIT;
+       odb_loose_path(odb, &path, oid->hash);
+       return check_and_freshen_file(path.buf, freshen);
+}
 
-       return check_and_freshen_file(buf.buf, freshen);
+static int check_and_freshen_local(const struct object_id *oid, int freshen)
+{
+       return check_and_freshen_odb(the_repository->objects->odb, oid, freshen);
 }
 
 static int check_and_freshen_nonlocal(const struct object_id *oid, int freshen)
 {
-       struct alternate_object_database *alt;
+       struct object_directory *odb;
+
        prepare_alt_odb(the_repository);
-       for (alt = the_repository->objects->alt_odb_list; alt; alt = alt->next) {
-               const char *path = alt_sha1_path(alt, oid->hash);
-               if (check_and_freshen_file(path, freshen))
+       for (odb = the_repository->objects->odb->next; odb; odb = odb->next) {
+               if (check_and_freshen_odb(odb, oid, freshen))
                        return 1;
        }
        return 0;
@@ -888,25 +945,17 @@ int git_open_cloexec(const char *name, int flags)
  *
  * The "path" out-parameter will give the path of the object we found (if any).
  * Note that it may point to static storage and is only valid until another
- * call to sha1_file_name(), etc.
+ * call to stat_sha1_file().
  */
 static int stat_sha1_file(struct repository *r, const unsigned char *sha1,
                          struct stat *st, const char **path)
 {
-       struct alternate_object_database *alt;
+       struct object_directory *odb;
        static struct strbuf buf = STRBUF_INIT;
 
-       strbuf_reset(&buf);
-       sha1_file_name(r, &buf, sha1);
-       *path = buf.buf;
-
-       if (!lstat(*path, st))
-               return 0;
-
        prepare_alt_odb(r);
-       errno = ENOENT;
-       for (alt = r->objects->alt_odb_list; alt; alt = alt->next) {
-               *path = alt_sha1_path(alt, sha1);
+       for (odb = r->objects->odb; odb; odb = odb->next) {
+               *path = odb_loose_path(odb, &buf, sha1);
                if (!lstat(*path, st))
                        return 0;
        }
@@ -922,25 +971,17 @@ static int open_sha1_file(struct repository *r,
                          const unsigned char *sha1, const char **path)
 {
        int fd;
-       struct alternate_object_database *alt;
-       int most_interesting_errno;
+       struct object_directory *odb;
+       int most_interesting_errno = ENOENT;
        static struct strbuf buf = STRBUF_INIT;
 
-       strbuf_reset(&buf);
-       sha1_file_name(r, &buf, sha1);
-       *path = buf.buf;
-
-       fd = git_open(*path);
-       if (fd >= 0)
-               return fd;
-       most_interesting_errno = errno;
-
        prepare_alt_odb(r);
-       for (alt = r->objects->alt_odb_list; alt; alt = alt->next) {
-               *path = alt_sha1_path(alt, sha1);
+       for (odb = r->objects->odb; odb; odb = odb->next) {
+               *path = odb_loose_path(odb, &buf, sha1);
                fd = git_open(*path);
                if (fd >= 0)
                        return fd;
+
                if (most_interesting_errno == ENOENT)
                        most_interesting_errno = errno;
        }
@@ -948,6 +989,22 @@ static int open_sha1_file(struct repository *r,
        return -1;
 }
 
+static int quick_has_loose(struct repository *r,
+                          const unsigned char *sha1)
+{
+       struct object_id oid;
+       struct object_directory *odb;
+
+       hashcpy(oid.hash, sha1);
+
+       prepare_alt_odb(r);
+       for (odb = r->objects->odb; odb; odb = odb->next) {
+               if (oid_array_lookup(odb_loose_cache(odb, &oid), &oid) >= 0)
+                       return 1;
+       }
+       return 0;
+}
+
 /*
  * Map the loose object at "path" if it is not NULL, or the path found by
  * searching for a loose object named "sha1".
@@ -971,6 +1028,7 @@ static void *map_sha1_file_1(struct repository *r, const char *path,
                        if (!*size) {
                                /* mmap() is forbidden on empty files */
                                error(_("object file %s is empty"), path);
+                               close(fd);
                                return NULL;
                        }
                        map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
@@ -1198,6 +1256,8 @@ static int sha1_loose_object_info(struct repository *r,
        if (!oi->typep && !oi->type_name && !oi->sizep && !oi->contentp) {
                const char *path;
                struct stat st;
+               if (!oi->disk_sizep && (flags & OBJECT_INFO_QUICK))
+                       return quick_has_loose(r, sha1) ? 0 : -1;
                if (stat_sha1_file(r, sha1, &st, &path) < 0)
                        return -1;
                if (oi->disk_sizep)
@@ -1361,7 +1421,9 @@ int oid_object_info(struct repository *r,
        return type;
 }
 
-static void *read_object(const unsigned char *sha1, enum object_type *type,
+static void *read_object(struct repository *r,
+                        const unsigned char *sha1,
+                        enum object_type *type,
                         unsigned long *size)
 {
        struct object_id oid;
@@ -1373,7 +1435,7 @@ static void *read_object(const unsigned char *sha1, enum object_type *type,
 
        hashcpy(oid.hash, sha1);
 
-       if (oid_object_info_extended(the_repository, &oid, &oi, 0) < 0)
+       if (oid_object_info_extended(r, &oid, &oi, 0) < 0)
                return NULL;
        return content;
 }
@@ -1401,7 +1463,8 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
  * deal with them should arrange to call read_object() and give error
  * messages themselves.
  */
-void *read_object_file_extended(const struct object_id *oid,
+void *read_object_file_extended(struct repository *r,
+                               const struct object_id *oid,
                                enum object_type *type,
                                unsigned long *size,
                                int lookup_replace)
@@ -1411,10 +1474,10 @@ void *read_object_file_extended(const struct object_id *oid,
        const char *path;
        struct stat st;
        const struct object_id *repl = lookup_replace ?
-               lookup_replace_object(the_repository, oid) : oid;
+               lookup_replace_object(r, oid) : oid;
 
        errno = 0;
-       data = read_object(repl->hash, type, size);
+       data = read_object(r, repl->hash, type, size);
        if (data)
                return data;
 
@@ -1426,11 +1489,11 @@ void *read_object_file_extended(const struct object_id *oid,
                die(_("replacement %s not found for %s"),
                    oid_to_hex(repl), oid_to_hex(oid));
 
-       if (!stat_sha1_file(the_repository, repl->hash, &st, &path))
+       if (!stat_sha1_file(r, repl->hash, &st, &path))
                die(_("loose object %s (stored in %s) is corrupt"),
                    oid_to_hex(repl), path);
 
-       if ((p = has_packed_and_bad(repl->hash)) != NULL)
+       if ((p = has_packed_and_bad(r, repl->hash)) != NULL)
                die(_("packed object %s (stored in %s) is corrupt"),
                    oid_to_hex(repl), p->pack_name);
 
@@ -1626,8 +1689,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
        static struct strbuf tmp_file = STRBUF_INIT;
        static struct strbuf filename = STRBUF_INIT;
 
-       strbuf_reset(&filename);
-       sha1_file_name(the_repository, &filename, oid->hash);
+       loose_object_path(the_repository, &filename, oid->hash);
 
        fd = create_tmpfile(&tmp_file, filename.buf);
        if (fd < 0) {
@@ -1755,7 +1817,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
 
        if (has_loose_object(oid))
                return 0;
-       buf = read_object(oid->hash, &type, &len);
+       buf = read_object(the_repository, oid->hash, &type, &len);
        if (!buf)
                return error(_("cannot read sha1_file for %s"), oid_to_hex(oid));
        hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(type), (uintmax_t)len) + 1;
@@ -1765,24 +1827,27 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
        return ret;
 }
 
-int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
+int repo_has_sha1_file_with_flags(struct repository *r,
+                                 const unsigned char *sha1, int flags)
 {
        struct object_id oid;
        if (!startup_info->have_repository)
                return 0;
        hashcpy(oid.hash, sha1);
-       return oid_object_info_extended(the_repository, &oid, NULL,
+       return oid_object_info_extended(r, &oid, NULL,
                                        flags | OBJECT_INFO_SKIP_CACHED) >= 0;
 }
 
-int has_object_file(const struct object_id *oid)
+int repo_has_object_file(struct repository *r,
+                        const struct object_id *oid)
 {
-       return has_sha1_file(oid->hash);
+       return repo_has_sha1_file(r, oid->hash);
 }
 
-int has_object_file_with_flags(const struct object_id *oid, int flags)
+int repo_has_object_file_with_flags(struct repository *r,
+                                   const struct object_id *oid, int flags)
 {
-       return has_sha1_file_with_flags(oid->hash, flags);
+       return repo_has_sha1_file_with_flags(r, oid->hash, flags);
 }
 
 static void check_tree(const void *buf, size_t size)
@@ -2134,43 +2199,63 @@ int for_each_loose_file_in_objdir(const char *path,
        return r;
 }
 
-struct loose_alt_odb_data {
-       each_loose_object_fn *cb;
-       void *data;
-};
+int for_each_loose_object(each_loose_object_fn cb, void *data,
+                         enum for_each_object_flags flags)
+{
+       struct object_directory *odb;
+
+       prepare_alt_odb(the_repository);
+       for (odb = the_repository->objects->odb; odb; odb = odb->next) {
+               int r = for_each_loose_file_in_objdir(odb->path, cb, NULL,
+                                                     NULL, data);
+               if (r)
+                       return r;
 
-static int loose_from_alt_odb(struct alternate_object_database *alt,
-                             void *vdata)
+               if (flags & FOR_EACH_OBJECT_LOCAL_ONLY)
+                       break;
+       }
+
+       return 0;
+}
+
+static int append_loose_object(const struct object_id *oid, const char *path,
+                              void *data)
 {
-       struct loose_alt_odb_data *data = vdata;
+       oid_array_append(data, oid);
+       return 0;
+}
+
+struct oid_array *odb_loose_cache(struct object_directory *odb,
+                                 const struct object_id *oid)
+{
+       int subdir_nr = oid->hash[0];
        struct strbuf buf = STRBUF_INIT;
-       int r;
 
-       strbuf_addstr(&buf, alt->path);
-       r = for_each_loose_file_in_objdir_buf(&buf,
-                                             data->cb, NULL, NULL,
-                                             data->data);
+       if (subdir_nr < 0 ||
+           subdir_nr >= ARRAY_SIZE(odb->loose_objects_subdir_seen))
+               BUG("subdir_nr out of range");
+
+       if (odb->loose_objects_subdir_seen[subdir_nr])
+               return &odb->loose_objects_cache[subdir_nr];
+
+       strbuf_addstr(&buf, odb->path);
+       for_each_file_in_obj_subdir(subdir_nr, &buf,
+                                   append_loose_object,
+                                   NULL, NULL,
+                                   &odb->loose_objects_cache[subdir_nr]);
+       odb->loose_objects_subdir_seen[subdir_nr] = 1;
        strbuf_release(&buf);
-       return r;
+       return &odb->loose_objects_cache[subdir_nr];
 }
 
-int for_each_loose_object(each_loose_object_fn cb, void *data,
-                         enum for_each_object_flags flags)
+void odb_clear_loose_cache(struct object_directory *odb)
 {
-       struct loose_alt_odb_data alt;
-       int r;
-
-       r = for_each_loose_file_in_objdir(get_object_directory(),
-                                         cb, NULL, NULL, data);
-       if (r)
-               return r;
-
-       if (flags & FOR_EACH_OBJECT_LOCAL_ONLY)
-               return 0;
+       int i;
 
-       alt.cb = cb;
-       alt.data = data;
-       return foreach_alt_odb(loose_from_alt_odb, &alt);
+       for (i = 0; i < ARRAY_SIZE(odb->loose_objects_cache); i++)
+               oid_array_clear(&odb->loose_objects_cache[i]);
+       memset(&odb->loose_objects_subdir_seen, 0,
+              sizeof(odb->loose_objects_subdir_seen));
 }
 
 static int check_stream_sha1(git_zstream *stream,
index faa60f69e311f5f09cd5b91a8fbbaf3e3e744ab0..4acd406b0ad7ed448878fe416c1d0221032ca65e 100644 (file)
@@ -83,51 +83,25 @@ static void update_candidates(struct disambiguate_state *ds, const struct object
        /* otherwise, current can be discarded and candidate is still good */
 }
 
-static int append_loose_object(const struct object_id *oid, const char *path,
-                              void *data)
-{
-       oid_array_append(data, oid);
-       return 0;
-}
-
 static int match_sha(unsigned, const unsigned char *, const unsigned char *);
 
 static void find_short_object_filename(struct disambiguate_state *ds)
 {
-       int subdir_nr = ds->bin_pfx.hash[0];
-       struct alternate_object_database *alt;
-       static struct alternate_object_database *fakeent;
+       struct object_directory *odb;
 
-       if (!fakeent) {
-               /*
-                * Create a "fake" alternate object database that
-                * points to our own object database, to make it
-                * easier to get a temporary working space in
-                * alt->name/alt->base while iterating over the
-                * object databases including our own.
-                */
-               fakeent = alloc_alt_odb(get_object_directory());
-       }
-       fakeent->next = the_repository->objects->alt_odb_list;
-
-       for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
+       for (odb = the_repository->objects->odb;
+            odb && !ds->ambiguous;
+            odb = odb->next) {
                int pos;
+               struct oid_array *loose_objects;
 
-               if (!alt->loose_objects_subdir_seen[subdir_nr]) {
-                       struct strbuf *buf = alt_scratch_buf(alt);
-                       for_each_file_in_obj_subdir(subdir_nr, buf,
-                                                   append_loose_object,
-                                                   NULL, NULL,
-                                                   &alt->loose_objects_cache);
-                       alt->loose_objects_subdir_seen[subdir_nr] = 1;
-               }
-
-               pos = oid_array_lookup(&alt->loose_objects_cache, &ds->bin_pfx);
+               loose_objects = odb_loose_cache(odb, &ds->bin_pfx);
+               pos = oid_array_lookup(loose_objects, &ds->bin_pfx);
                if (pos < 0)
                        pos = -1 - pos;
-               while (!ds->ambiguous && pos < alt->loose_objects_cache.nr) {
+               while (!ds->ambiguous && pos < loose_objects->nr) {
                        const struct object_id *oid;
-                       oid = alt->loose_objects_cache.oid + pos;
+                       oid = loose_objects->oid + pos;
                        if (!match_sha(ds->len, ds->bin_pfx.hash, oid->hash))
                                break;
                        update_candidates(ds, oid);
@@ -216,9 +190,6 @@ static void find_short_packed_object(struct disambiguate_state *ds)
                unique_in_pack(p, ds);
 }
 
-#define SHORT_NAME_NOT_FOUND (-1)
-#define SHORT_NAME_AMBIGUOUS (-2)
-
 static int finish_object_disambiguation(struct disambiguate_state *ds,
                                        struct object_id *oid)
 {
@@ -226,7 +197,7 @@ static int finish_object_disambiguation(struct disambiguate_state *ds,
                return SHORT_NAME_AMBIGUOUS;
 
        if (!ds->candidate_exists)
-               return SHORT_NAME_NOT_FOUND;
+               return MISSING_OBJECT;
 
        if (!ds->candidate_checked)
                /*
@@ -440,8 +411,9 @@ static int sort_ambiguous(const void *a, const void *b)
        return a_type_sort > b_type_sort ? 1 : -1;
 }
 
-static int get_short_oid(const char *name, int len, struct object_id *oid,
-                         unsigned flags)
+static enum get_oid_result get_short_oid(const char *name, int len,
+                                        struct object_id *oid,
+                                        unsigned flags)
 {
        int status;
        struct disambiguate_state ds;
@@ -759,7 +731,7 @@ static inline int push_mark(const char *string, int len)
        return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
 }
 
-static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags);
+static enum get_oid_result get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags);
 static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
 
 static int get_oid_basic(const char *str, int len, struct object_id *oid,
@@ -909,11 +881,12 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
        return 0;
 }
 
-static int get_parent(const char *name, int len,
-                     struct object_id *result, int idx)
+static enum get_oid_result get_parent(const char *name, int len,
+                                     struct object_id *result, int idx)
 {
        struct object_id oid;
-       int ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH);
+       enum get_oid_result ret = get_oid_1(name, len, &oid,
+                                           GET_OID_COMMITTISH);
        struct commit *commit;
        struct commit_list *p;
 
@@ -921,24 +894,25 @@ static int get_parent(const char *name, int len,
                return ret;
        commit = lookup_commit_reference(the_repository, &oid);
        if (parse_commit(commit))
-               return -1;
+               return MISSING_OBJECT;
        if (!idx) {
                oidcpy(result, &commit->object.oid);
-               return 0;
+               return FOUND;
        }
        p = commit->parents;
        while (p) {
                if (!--idx) {
                        oidcpy(result, &p->item->object.oid);
-                       return 0;
+                       return FOUND;
                }
                p = p->next;
        }
-       return -1;
+       return MISSING_OBJECT;
 }
 
-static int get_nth_ancestor(const char *name, int len,
-                           struct object_id *result, int generation)
+static enum get_oid_result get_nth_ancestor(const char *name, int len,
+                                           struct object_id *result,
+                                           int generation)
 {
        struct object_id oid;
        struct commit *commit;
@@ -949,15 +923,15 @@ static int get_nth_ancestor(const char *name, int len,
                return ret;
        commit = lookup_commit_reference(the_repository, &oid);
        if (!commit)
-               return -1;
+               return MISSING_OBJECT;
 
        while (generation--) {
                if (parse_commit(commit) || !commit->parents)
-                       return -1;
+                       return MISSING_OBJECT;
                commit = commit->parents->item;
        }
        oidcpy(result, &commit->object.oid);
-       return 0;
+       return FOUND;
 }
 
 struct object *peel_to_type(const char *name, int namelen,
@@ -1103,7 +1077,9 @@ static int get_describe_name(const char *name, int len, struct object_id *oid)
        return -1;
 }
 
-static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags)
+static enum get_oid_result get_oid_1(const char *name, int len,
+                                    struct object_id *oid,
+                                    unsigned lookup_flags)
 {
        int ret, has_suffix;
        const char *cp;
@@ -1137,16 +1113,16 @@ static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned
 
        ret = peel_onion(name, len, oid, lookup_flags);
        if (!ret)
-               return 0;
+               return FOUND;
 
        ret = get_oid_basic(name, len, oid, lookup_flags);
        if (!ret)
-               return 0;
+               return FOUND;
 
        /* It could be describe output that is "SOMETHING-gXXXX" */
        ret = get_describe_name(name, len, oid);
        if (!ret)
-               return 0;
+               return FOUND;
 
        return get_short_oid(name, len, oid, lookup_flags);
 }
@@ -1690,11 +1666,11 @@ static char *resolve_relative_path(const char *rel)
                           rel);
 }
 
-static int get_oid_with_context_1(const char *name,
-                                 unsigned flags,
-                                 const char *prefix,
-                                 struct object_id *oid,
-                                 struct object_context *oc)
+static enum get_oid_result get_oid_with_context_1(const char *name,
+                                                 unsigned flags,
+                                                 const char *prefix,
+                                                 struct object_id *oid,
+                                                 struct object_context *oc)
 {
        int ret, bracket_depth;
        int namelen = strlen(name);
diff --git a/sha256/block/sha256.c b/sha256/block/sha256.c
new file mode 100644 (file)
index 0000000..37850b4
--- /dev/null
@@ -0,0 +1,196 @@
+#include "git-compat-util.h"
+#include "./sha256.h"
+
+#undef RND
+#undef BLKSIZE
+
+#define BLKSIZE blk_SHA256_BLKSIZE
+
+void blk_SHA256_Init(blk_SHA256_CTX *ctx)
+{
+       ctx->offset = 0;
+       ctx->size = 0;
+       ctx->state[0] = 0x6a09e667ul;
+       ctx->state[1] = 0xbb67ae85ul;
+       ctx->state[2] = 0x3c6ef372ul;
+       ctx->state[3] = 0xa54ff53aul;
+       ctx->state[4] = 0x510e527ful;
+       ctx->state[5] = 0x9b05688cul;
+       ctx->state[6] = 0x1f83d9abul;
+       ctx->state[7] = 0x5be0cd19ul;
+}
+
+static inline uint32_t ror(uint32_t x, unsigned n)
+{
+       return (x >> n) | (x << (32 - n));
+}
+
+static inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z)
+{
+       return z ^ (x & (y ^ z));
+}
+
+static inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z)
+{
+       return ((x | y) & z) | (x & y);
+}
+
+static inline uint32_t sigma0(uint32_t x)
+{
+       return ror(x, 2) ^ ror(x, 13) ^ ror(x, 22);
+}
+
+static inline uint32_t sigma1(uint32_t x)
+{
+       return ror(x, 6) ^ ror(x, 11) ^ ror(x, 25);
+}
+
+static inline uint32_t gamma0(uint32_t x)
+{
+       return ror(x, 7) ^ ror(x, 18) ^ (x >> 3);
+}
+
+static inline uint32_t gamma1(uint32_t x)
+{
+       return ror(x, 17) ^ ror(x, 19) ^ (x >> 10);
+}
+
+static void blk_SHA256_Transform(blk_SHA256_CTX *ctx, const unsigned char *buf)
+{
+
+       uint32_t S[8], W[64], t0, t1;
+       int i;
+
+       /* copy state into S */
+       for (i = 0; i < 8; i++)
+               S[i] = ctx->state[i];
+
+       /* copy the state into 512-bits into W[0..15] */
+       for (i = 0; i < 16; i++, buf += sizeof(uint32_t))
+               W[i] = get_be32(buf);
+
+       /* fill W[16..63] */
+       for (i = 16; i < 64; i++)
+               W[i] = gamma1(W[i - 2]) + W[i - 7] + gamma0(W[i - 15]) + W[i - 16];
+
+#define RND(a,b,c,d,e,f,g,h,i,ki)                    \
+       t0 = h + sigma1(e) + ch(e, f, g) + ki + W[i];   \
+       t1 = sigma0(a) + maj(a, b, c);                  \
+       d += t0;                                        \
+       h  = t0 + t1;
+
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2);
+
+       for (i = 0; i < 8; i++)
+               ctx->state[i] += S[i];
+}
+
+void blk_SHA256_Update(blk_SHA256_CTX *ctx, const void *data, size_t len)
+{
+       unsigned int len_buf = ctx->size & 63;
+
+       ctx->size += len;
+
+       /* Read the data into buf and process blocks as they get full */
+       if (len_buf) {
+               unsigned int left = 64 - len_buf;
+               if (len < left)
+                       left = len;
+               memcpy(len_buf + ctx->buf, data, left);
+               len_buf = (len_buf + left) & 63;
+               len -= left;
+               data = ((const char *)data + left);
+               if (len_buf)
+                       return;
+               blk_SHA256_Transform(ctx, ctx->buf);
+       }
+       while (len >= 64) {
+               blk_SHA256_Transform(ctx, data);
+               data = ((const char *)data + 64);
+               len -= 64;
+       }
+       if (len)
+               memcpy(ctx->buf, data, len);
+}
+
+void blk_SHA256_Final(unsigned char *digest, blk_SHA256_CTX *ctx)
+{
+       static const unsigned char pad[64] = { 0x80 };
+       unsigned int padlen[2];
+       int i;
+
+       /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
+       padlen[0] = htonl((uint32_t)(ctx->size >> 29));
+       padlen[1] = htonl((uint32_t)(ctx->size << 3));
+
+       i = ctx->size & 63;
+       blk_SHA256_Update(ctx, pad, 1 + (63 & (55 - i)));
+       blk_SHA256_Update(ctx, padlen, 8);
+
+       /* copy output */
+       for (i = 0; i < 8; i++, digest += sizeof(uint32_t))
+               put_be32(digest, ctx->state[i]);
+}
diff --git a/sha256/block/sha256.h b/sha256/block/sha256.h
new file mode 100644 (file)
index 0000000..5099d64
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef SHA256_BLOCK_SHA256_H
+#define SHA256_BLOCK_SHA256_H
+
+#define blk_SHA256_BLKSIZE 64
+
+struct blk_SHA256_CTX {
+       uint32_t state[8];
+       uint64_t size;
+       uint32_t offset;
+       uint8_t buf[blk_SHA256_BLKSIZE];
+};
+
+typedef struct blk_SHA256_CTX blk_SHA256_CTX;
+
+void blk_SHA256_Init(blk_SHA256_CTX *ctx);
+void blk_SHA256_Update(blk_SHA256_CTX *ctx, const void *data, size_t len);
+void blk_SHA256_Final(unsigned char *digest, blk_SHA256_CTX *ctx);
+
+#define platform_SHA256_CTX blk_SHA256_CTX
+#define platform_SHA256_Init blk_SHA256_Init
+#define platform_SHA256_Update blk_SHA256_Update
+#define platform_SHA256_Final blk_SHA256_Final
+
+#endif
diff --git a/sha256/gcrypt.h b/sha256/gcrypt.h
new file mode 100644 (file)
index 0000000..09bd8bb
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef SHA256_GCRYPT_H
+#define SHA256_GCRYPT_H
+
+#include <gcrypt.h>
+
+#define SHA256_DIGEST_SIZE 32
+
+typedef gcry_md_hd_t gcrypt_SHA256_CTX;
+
+inline void gcrypt_SHA256_Init(gcrypt_SHA256_CTX *ctx)
+{
+       gcry_md_open(ctx, GCRY_MD_SHA256, 0);
+}
+
+inline void gcrypt_SHA256_Update(gcrypt_SHA256_CTX *ctx, const void *data, size_t len)
+{
+       gcry_md_write(*ctx, data, len);
+}
+
+inline void gcrypt_SHA256_Final(unsigned char *digest, gcrypt_SHA256_CTX *ctx)
+{
+       memcpy(digest, gcry_md_read(*ctx, GCRY_MD_SHA256), SHA256_DIGEST_SIZE);
+}
+
+#define platform_SHA256_CTX gcrypt_SHA256_CTX
+#define platform_SHA256_Init gcrypt_SHA256_Init
+#define platform_SHA256_Update gcrypt_SHA256_Update
+#define platform_SHA256_Final gcrypt_SHA256_Final
+
+#endif
index 02fdbfc554c462c1eecdf1ddc6d17edbc1d8d853..ce45297940d417e3454b08d3f0c29f5cc6d93658 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -43,6 +43,13 @@ int register_shallow(struct repository *r, const struct object_id *oid)
 
 int is_repository_shallow(struct repository *r)
 {
+       /*
+        * NEEDSWORK: This function updates
+        * r->parsed_objects->{is_shallow,shallow_stat} as a side effect but
+        * there is no corresponding function to clear them when the shallow
+        * file is updated.
+        */
+
        FILE *fp;
        char buf[1024];
        const char *path = r->parsed_objects->alternate_shallow_file;
index 368647acf8c04decee14ceea2f03c535a206ad18..ef851113c44ee91e594734bf68567110b7ac8c6b 100644 (file)
@@ -1,7 +1,6 @@
 #include "cache.h"
 #include "color.h"
 #include "config.h"
-#include "pkt-line.h"
 #include "sideband.h"
 #include "help.h"
 
@@ -87,7 +86,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
                struct keyword_entry *p = keywords + i;
                int len = strlen(p->keyword);
 
-               if (n <= len)
+               if (n < len)
                        continue;
                /*
                 * Match case insensitively, so we colorize output from existing
@@ -95,7 +94,8 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
                 * messages. We only highlight the word precisely, so
                 * "successful" stays uncolored.
                 */
-               if (!strncasecmp(p->keyword, src, len) && !isalnum(src[len])) {
+               if (!strncasecmp(p->keyword, src, len) &&
+                   (len == n || !isalnum(src[len]))) {
                        strbuf_addstr(dest, p->color);
                        strbuf_add(dest, src, len);
                        strbuf_addstr(dest, GIT_COLOR_RESET);
@@ -109,109 +109,104 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
 }
 
 
-/*
- * Receive multiplexed output stream over git native protocol.
- * in_stream is the input stream from the remote, which carries data
- * in pkt_line format with band designator.  Demultiplex it into out
- * and err and return error appropriately.  Band #1 carries the
- * primary payload.  Things coming over band #2 is not necessarily
- * error; they are usually informative message on the standard error
- * stream, aka "verbose").  A message over band #3 is a signal that
- * the remote died unexpectedly.  A flush() concludes the stream.
- */
-
 #define DISPLAY_PREFIX "remote: "
 
 #define ANSI_SUFFIX "\033[K"
 #define DUMB_SUFFIX "        "
 
-int recv_sideband(const char *me, int in_stream, int out)
+int demultiplex_sideband(const char *me, char *buf, int len,
+                        int die_on_error,
+                        struct strbuf *scratch,
+                        enum sideband_type *sideband_type)
 {
-       const char *suffix;
-       char buf[LARGE_PACKET_MAX + 1];
-       struct strbuf outbuf = STRBUF_INIT;
-       int retval = 0;
-
-       if (isatty(2) && !is_terminal_dumb())
-               suffix = ANSI_SUFFIX;
-       else
-               suffix = DUMB_SUFFIX;
-
-       while (!retval) {
-               const char *b, *brk;
-               int band, len;
-               len = packet_read(in_stream, NULL, NULL, buf, LARGE_PACKET_MAX, 0);
-               if (len == 0)
-                       break;
-               if (len < 1) {
-                       strbuf_addf(&outbuf,
-                                   "%s%s: protocol error: no band designator",
-                                   outbuf.len ? "\n" : "", me);
-                       retval = SIDEBAND_PROTOCOL_ERROR;
-                       break;
-               }
-               band = buf[0] & 0xff;
-               buf[len] = '\0';
-               len--;
-               switch (band) {
-               case 3:
-                       strbuf_addf(&outbuf, "%s%s", outbuf.len ? "\n" : "",
-                                   DISPLAY_PREFIX);
-                       maybe_colorize_sideband(&outbuf, buf + 1, len);
-
-                       retval = SIDEBAND_REMOTE_ERROR;
-                       break;
-               case 2:
-                       b = buf + 1;
-
-                       /*
-                        * Append a suffix to each nonempty line to clear the
-                        * end of the screen line.
-                        *
-                        * The output is accumulated in a buffer and
-                        * each line is printed to stderr using
-                        * write(2) to ensure inter-process atomicity.
-                        */
-                       while ((brk = strpbrk(b, "\n\r"))) {
-                               int linelen = brk - b;
-
-                               if (!outbuf.len)
-                                       strbuf_addstr(&outbuf, DISPLAY_PREFIX);
-                               if (linelen > 0) {
-                                       maybe_colorize_sideband(&outbuf, b, linelen);
-                                       strbuf_addstr(&outbuf, suffix);
-                               }
-
-                               strbuf_addch(&outbuf, *brk);
-                               xwrite(2, outbuf.buf, outbuf.len);
-                               strbuf_reset(&outbuf);
-
-                               b = brk + 1;
-                       }
+       static const char *suffix;
+       const char *b, *brk;
+       int band;
+
+       if (!suffix) {
+               if (isatty(2) && !is_terminal_dumb())
+                       suffix = ANSI_SUFFIX;
+               else
+                       suffix = DUMB_SUFFIX;
+       }
 
-                       if (*b) {
-                               strbuf_addstr(&outbuf, outbuf.len ?
-                                           "" : DISPLAY_PREFIX);
-                               maybe_colorize_sideband(&outbuf, b, strlen(b));
+       if (len == 0) {
+               *sideband_type = SIDEBAND_FLUSH;
+               goto cleanup;
+       }
+       if (len < 1) {
+               strbuf_addf(scratch,
+                           "%s%s: protocol error: no band designator",
+                           scratch->len ? "\n" : "", me);
+               *sideband_type = SIDEBAND_PROTOCOL_ERROR;
+               goto cleanup;
+       }
+       band = buf[0] & 0xff;
+       buf[len] = '\0';
+       len--;
+       switch (band) {
+       case 3:
+               if (die_on_error)
+                       die("remote error: %s", buf + 1);
+               strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "",
+                           DISPLAY_PREFIX);
+               maybe_colorize_sideband(scratch, buf + 1, len);
+
+               *sideband_type = SIDEBAND_REMOTE_ERROR;
+               break;
+       case 2:
+               b = buf + 1;
+
+               /*
+                * Append a suffix to each nonempty line to clear the
+                * end of the screen line.
+                *
+                * The output is accumulated in a buffer and
+                * each line is printed to stderr using
+                * write(2) to ensure inter-process atomicity.
+                */
+               while ((brk = strpbrk(b, "\n\r"))) {
+                       int linelen = brk - b;
+
+                       if (!scratch->len)
+                               strbuf_addstr(scratch, DISPLAY_PREFIX);
+                       if (linelen > 0) {
+                               maybe_colorize_sideband(scratch, b, linelen);
+                               strbuf_addstr(scratch, suffix);
                        }
-                       break;
-               case 1:
-                       write_or_die(out, buf + 1, len);
-                       break;
-               default:
-                       strbuf_addf(&outbuf, "%s%s: protocol error: bad band #%d",
-                                   outbuf.len ? "\n" : "", me, band);
-                       retval = SIDEBAND_PROTOCOL_ERROR;
-                       break;
+
+                       strbuf_addch(scratch, *brk);
+                       xwrite(2, scratch->buf, scratch->len);
+                       strbuf_reset(scratch);
+
+                       b = brk + 1;
+               }
+
+               if (*b) {
+                       strbuf_addstr(scratch, scratch->len ?
+                                   "" : DISPLAY_PREFIX);
+                       maybe_colorize_sideband(scratch, b, strlen(b));
                }
+               return 0;
+       case 1:
+               *sideband_type = SIDEBAND_PRIMARY;
+               break;
+       default:
+               strbuf_addf(scratch, "%s%s: protocol error: bad band #%d",
+                           scratch->len ? "\n" : "", me, band);
+               *sideband_type = SIDEBAND_PROTOCOL_ERROR;
+               break;
        }
 
-       if (outbuf.len) {
-               strbuf_addch(&outbuf, '\n');
-               xwrite(2, outbuf.buf, outbuf.len);
+cleanup:
+       if (die_on_error && *sideband_type == SIDEBAND_PROTOCOL_ERROR)
+               die("%s", scratch->buf);
+       if (scratch->len) {
+               strbuf_addch(scratch, '\n');
+               xwrite(2, scratch->buf, scratch->len);
        }
-       strbuf_release(&outbuf);
-       return retval;
+       strbuf_release(scratch);
+       return 1;
 }
 
 /*
index 7a8146f161b7b460d29baea82230c0b6c11c5322..227740a58e58bd25c4625bc1963106542bd99779 100644 (file)
@@ -1,10 +1,29 @@
 #ifndef SIDEBAND_H
 #define SIDEBAND_H
 
-#define SIDEBAND_PROTOCOL_ERROR -2
-#define SIDEBAND_REMOTE_ERROR -1
+enum sideband_type {
+       SIDEBAND_PROTOCOL_ERROR = -2,
+       SIDEBAND_REMOTE_ERROR = -1,
+       SIDEBAND_FLUSH = 0,
+       SIDEBAND_PRIMARY = 1
+};
+
+/*
+ * Inspects a multiplexed packet read from the remote. If this packet is a
+ * progress packet and thus should not be processed by the caller, returns 0.
+ * Otherwise, returns 1, releases scratch, and sets sideband_type.
+ *
+ * If this packet is SIDEBAND_PROTOCOL_ERROR, SIDEBAND_REMOTE_ERROR, or a
+ * progress packet, also prints a message to stderr.
+ *
+ * scratch must be a struct strbuf allocated by the caller. It is used to store
+ * progress messages split across multiple packets.
+ */
+int demultiplex_sideband(const char *me, char *buf, int len,
+                        int die_on_error,
+                        struct strbuf *scratch,
+                        enum sideband_type *sideband_type);
 
-int recv_sideband(const char *me, int in_stream, int out);
 void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
 
 #endif
index ac7c7a22f90b7320a235f23db9387fd91ba5b2cd..891f82f51a753d065ce67b111bce86e790801599 100644 (file)
@@ -499,7 +499,7 @@ static struct stream_vtbl incore_vtbl = {
 
 static open_method_decl(incore)
 {
-       st->u.incore.buf = read_object_file_extended(oid, type, &st->size, 0);
+       st->u.incore.buf = read_object_file_extended(the_repository, oid, type, &st->size, 0);
        st->u.incore.read_ptr = 0;
        st->vtbl = &incore_vtbl;
 
index 1f6063f2a27812ee27b5d510dc066249198b48e3..a917955fbd8d18956ae593b5fa190042e13db644 100644 (file)
@@ -155,7 +155,8 @@ static int item_is_not_empty(struct string_list_item *item, void *unused)
        return *item->string != '\0';
 }
 
-void string_list_remove_empty_items(struct string_list *list, int free_util) {
+void string_list_remove_empty_items(struct string_list *list, int free_util)
+{
        filter_string_list(list, free_util, item_is_not_empty, NULL);
 }
 
index 6415cc55807c7ed9ee4cbcaa57f41a5804b6e854..a5f2694a5f651a51b3012ce4a454e63698a7e0e7 100644 (file)
@@ -25,7 +25,6 @@
 #include "commit-reach.h"
 
 static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
-static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP;
 static int initialized_fetch_ref_tips;
 static struct oid_array ref_tips_before_fetch;
 static struct oid_array ref_tips_after_fetch;
@@ -457,7 +456,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
        return prepare_revision_walk(rev);
 }
 
-static void print_submodule_summary(struct rev_info *rev, struct diff_options *o)
+static void print_submodule_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
 {
        static const char format[] = "  %m %s";
        struct strbuf sb = STRBUF_INIT;
@@ -468,7 +467,8 @@ static void print_submodule_summary(struct rev_info *rev, struct diff_options *o
                ctx.date_mode = rev->date_mode;
                ctx.output_encoding = get_log_output_encoding();
                strbuf_setlen(&sb, 0);
-               format_commit_message(commit, format, &sb, &ctx);
+               repo_format_commit_message(r, commit, format, &sb,
+                                     &ctx);
                strbuf_addch(&sb, '\n');
                if (commit->object.flags & SYMMETRIC_LEFT)
                        diff_emit_submodule_del(o, sb.buf);
@@ -495,14 +495,52 @@ void prepare_submodule_repo_env(struct argv_array *out)
                         DEFAULT_GIT_DIR_ENVIRONMENT);
 }
 
-/* Helper function to display the submodule header line prior to the full
- * summary output. If it can locate the submodule objects directory it will
- * attempt to lookup both the left and right commits and put them into the
- * left and right pointers.
+static void prepare_submodule_repo_env_in_gitdir(struct argv_array *out)
+{
+       prepare_submodule_repo_env_no_git_dir(out);
+       argv_array_pushf(out, "%s=.", GIT_DIR_ENVIRONMENT);
+}
+
+/*
+ * Initialize a repository struct for a submodule based on the provided 'path'.
+ *
+ * Unlike repo_submodule_init, this tolerates submodules not present
+ * in .gitmodules. This function exists only to preserve historical behavior,
+ *
+ * Returns the repository struct on success,
+ * NULL when the submodule is not present.
  */
-static void show_submodule_header(struct diff_options *o, const char *path,
+static struct repository *open_submodule(const char *path)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct repository *out = xmalloc(sizeof(*out));
+
+       if (submodule_to_gitdir(&sb, path) || repo_init(out, sb.buf, NULL)) {
+               strbuf_release(&sb);
+               free(out);
+               return NULL;
+       }
+
+       /* Mark it as a submodule */
+       out->submodule_prefix = xstrdup(path);
+
+       strbuf_release(&sb);
+       return out;
+}
+
+/*
+ * Helper function to display the submodule header line prior to the full
+ * summary output.
+ *
+ * If it can locate the submodule git directory it will create a repository
+ * handle for the submodule and lookup both the left and right commits and
+ * put them into the left and right pointers.
+ */
+static void show_submodule_header(struct diff_options *o,
+               const char *path,
                struct object_id *one, struct object_id *two,
                unsigned dirty_submodule,
+               struct repository *sub,
                struct commit **left, struct commit **right,
                struct commit_list **merge_bases)
 {
@@ -521,7 +559,7 @@ static void show_submodule_header(struct diff_options *o, const char *path,
        else if (is_null_oid(two))
                message = "(submodule deleted)";
 
-       if (add_submodule_odb(path)) {
+       if (!sub) {
                if (!message)
                        message = "(commits not present)";
                goto output_header;
@@ -531,8 +569,8 @@ static void show_submodule_header(struct diff_options *o, const char *path,
         * Attempt to lookup the commit references, and determine if this is
         * a fast forward or fast backwards update.
         */
-       *left = lookup_commit_reference(the_repository, one);
-       *right = lookup_commit_reference(the_repository, two);
+       *left = lookup_commit_reference(sub, one);
+       *right = lookup_commit_reference(sub, two);
 
        /*
         * Warn about missing commits in the submodule project, but only if
@@ -542,7 +580,7 @@ static void show_submodule_header(struct diff_options *o, const char *path,
             (!is_null_oid(two) && !*right))
                message = "(commits not present)";
 
-       *merge_bases = get_merge_bases(*left, *right);
+       *merge_bases = repo_get_merge_bases(sub, *left, *right);
        if (*merge_bases) {
                if ((*merge_bases)->item == *left)
                        fast_forward = 1;
@@ -576,16 +614,18 @@ void show_submodule_summary(struct diff_options *o, const char *path,
        struct rev_info rev;
        struct commit *left = NULL, *right = NULL;
        struct commit_list *merge_bases = NULL;
+       struct repository *sub;
 
+       sub = open_submodule(path);
        show_submodule_header(o, path, one, two, dirty_submodule,
-                             &left, &right, &merge_bases);
+                             sub, &left, &right, &merge_bases);
 
        /*
         * If we don't have both a left and a right pointer, there is no
         * reason to try and display a summary. The header line should contain
         * all the information the user needs.
         */
-       if (!left || !right)
+       if (!left || !right || !sub)
                goto out;
 
        /* Treat revision walker failure the same as missing commits */
@@ -594,13 +634,17 @@ void show_submodule_summary(struct diff_options *o, const char *path,
                goto out;
        }
 
-       print_submodule_summary(&rev, o);
+       print_submodule_summary(sub, &rev, o);
 
 out:
        if (merge_bases)
                free_commit_list(merge_bases);
        clear_commit_marks(left, ~0);
        clear_commit_marks(right, ~0);
+       if (sub) {
+               repo_clear(sub);
+               free(sub);
+       }
 }
 
 void show_submodule_inline_diff(struct diff_options *o, const char *path,
@@ -612,9 +656,11 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,
        struct commit_list *merge_bases = NULL;
        struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf sb = STRBUF_INIT;
+       struct repository *sub;
 
+       sub = open_submodule(path);
        show_submodule_header(o, path, one, two, dirty_submodule,
-                             &left, &right, &merge_bases);
+                             sub, &left, &right, &merge_bases);
 
        /* We need a valid left and right commit to display a difference */
        if (!(left || is_null_oid(one)) ||
@@ -675,6 +721,10 @@ done:
                clear_commit_marks(left, ~0);
        if (right)
                clear_commit_marks(right, ~0);
+       if (sub) {
+               repo_clear(sub);
+               free(sub);
+       }
 }
 
 int should_update_submodules(void)
@@ -999,9 +1049,6 @@ static int push_submodule(const char *path,
                          const struct string_list *push_options,
                          int dry_run)
 {
-       if (add_submodule_odb(path))
-               return 1;
-
        if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
                struct child_process cp = CHILD_PROCESS_INIT;
                argv_array_push(&cp.args, "push");
@@ -1136,11 +1183,11 @@ void check_for_new_submodule_commits(struct object_id *oid)
        oid_array_append(&ref_tips_after_fetch, oid);
 }
 
-static void calculate_changed_submodule_paths(struct repository *r)
+static void calculate_changed_submodule_paths(struct repository *r,
+               struct string_list *changed_submodule_names)
 {
        struct argv_array argv = ARGV_ARRAY_INIT;
-       struct string_list changed_submodules = STRING_LIST_INIT_DUP;
-       const struct string_list_item *name;
+       struct string_list_item *name;
 
        /* No need to check if there are no submodules configured */
        if (!submodule_from_path(r, NULL, NULL))
@@ -1157,9 +1204,9 @@ static void calculate_changed_submodule_paths(struct repository *r)
         * Collect all submodules (whether checked out or not) for which new
         * commits have been recorded upstream in "changed_submodule_names".
         */
-       collect_changed_submodules(r, &changed_submodules, &argv);
+       collect_changed_submodules(r, changed_submodule_names, &argv);
 
-       for_each_string_list_item(name, &changed_submodules) {
+       for_each_string_list_item(name, changed_submodule_names) {
                struct oid_array *commits = name->util;
                const struct submodule *submodule;
                const char *path = NULL;
@@ -1173,11 +1220,14 @@ static void calculate_changed_submodule_paths(struct repository *r)
                if (!path)
                        continue;
 
-               if (!submodule_has_commits(r, path, commits))
-                       string_list_append(&changed_submodule_names, name->string);
+               if (submodule_has_commits(r, path, commits)) {
+                       oid_array_clear(commits);
+                       *name->string = '\0';
+               }
        }
 
-       free_submodules_oids(&changed_submodules);
+       string_list_remove_empty_items(changed_submodule_names, 1);
+
        argv_array_clear(&argv);
        oid_array_clear(&ref_tips_before_fetch);
        oid_array_clear(&ref_tips_after_fetch);
@@ -1221,8 +1271,16 @@ struct submodule_parallel_fetch {
        int default_option;
        int quiet;
        int result;
+
+       struct string_list changed_submodule_names;
+
+       /* Pending fetches by OIDs */
+       struct fetch_task **oid_fetch_tasks;
+       int oid_fetch_tasks_nr, oid_fetch_tasks_alloc;
 };
-#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0}
+#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0, \
+                 STRING_LIST_INIT_DUP, \
+                 NULL, 0, 0}
 
 static int get_fetch_recurse_config(const struct submodule *submodule,
                                    struct submodule_parallel_fetch *spf)
@@ -1249,40 +1307,126 @@ static int get_fetch_recurse_config(const struct submodule *submodule,
        return spf->default_option;
 }
 
+/*
+ * Fetch in progress (if callback data) or
+ * pending (if in oid_fetch_tasks in struct submodule_parallel_fetch)
+ */
+struct fetch_task {
+       struct repository *repo;
+       const struct submodule *sub;
+       unsigned free_sub : 1; /* Do we need to free the submodule? */
+
+       struct oid_array *commits; /* Ensure these commits are fetched */
+};
+
+/**
+ * When a submodule is not defined in .gitmodules, we cannot access it
+ * via the regular submodule-config. Create a fake submodule, which we can
+ * work on.
+ */
+static const struct submodule *get_non_gitmodules_submodule(const char *path)
+{
+       struct submodule *ret = NULL;
+       const char *name = default_name_or_path(path);
+
+       if (!name)
+               return NULL;
+
+       ret = xmalloc(sizeof(*ret));
+       memset(ret, 0, sizeof(*ret));
+       ret->path = name;
+       ret->name = name;
+
+       return (const struct submodule *) ret;
+}
+
+static struct fetch_task *fetch_task_create(struct repository *r,
+                                           const char *path)
+{
+       struct fetch_task *task = xmalloc(sizeof(*task));
+       memset(task, 0, sizeof(*task));
+
+       task->sub = submodule_from_path(r, &null_oid, path);
+       if (!task->sub) {
+               /*
+                * No entry in .gitmodules? Technically not a submodule,
+                * but historically we supported repositories that happen to be
+                * in-place where a gitlink is. Keep supporting them.
+                */
+               task->sub = get_non_gitmodules_submodule(path);
+               if (!task->sub) {
+                       free(task);
+                       return NULL;
+               }
+
+               task->free_sub = 1;
+       }
+
+       return task;
+}
+
+static void fetch_task_release(struct fetch_task *p)
+{
+       if (p->free_sub)
+               free((void*)p->sub);
+       p->free_sub = 0;
+       p->sub = NULL;
+
+       if (p->repo)
+               repo_clear(p->repo);
+       FREE_AND_NULL(p->repo);
+}
+
+static struct repository *get_submodule_repo_for(struct repository *r,
+                                                const struct submodule *sub)
+{
+       struct repository *ret = xmalloc(sizeof(*ret));
+
+       if (repo_submodule_init(ret, r, sub)) {
+               /*
+                * No entry in .gitmodules? Technically not a submodule,
+                * but historically we supported repositories that happen to be
+                * in-place where a gitlink is. Keep supporting them.
+                */
+               struct strbuf gitdir = STRBUF_INIT;
+               strbuf_repo_worktree_path(&gitdir, r, "%s/.git", sub->path);
+               if (repo_init(ret, gitdir.buf, NULL)) {
+                       strbuf_release(&gitdir);
+                       free(ret);
+                       return NULL;
+               }
+               strbuf_release(&gitdir);
+       }
+
+       return ret;
+}
+
 static int get_next_submodule(struct child_process *cp,
                              struct strbuf *err, void *data, void **task_cb)
 {
-       int ret = 0;
        struct submodule_parallel_fetch *spf = data;
 
        for (; spf->count < spf->r->index->cache_nr; spf->count++) {
-               struct strbuf submodule_path = STRBUF_INIT;
-               struct strbuf submodule_git_dir = STRBUF_INIT;
-               struct strbuf submodule_prefix = STRBUF_INIT;
                const struct cache_entry *ce = spf->r->index->cache[spf->count];
-               const char *git_dir, *default_argv;
-               const struct submodule *submodule;
-               struct submodule default_submodule = SUBMODULE_INIT;
+               const char *default_argv;
+               struct fetch_task *task;
 
                if (!S_ISGITLINK(ce->ce_mode))
                        continue;
 
-               submodule = submodule_from_path(spf->r, &null_oid, ce->name);
-               if (!submodule) {
-                       const char *name = default_name_or_path(ce->name);
-                       if (name) {
-                               default_submodule.path = default_submodule.name = name;
-                               submodule = &default_submodule;
-                       }
-               }
+               task = fetch_task_create(spf->r, ce->name);
+               if (!task)
+                       continue;
 
-               switch (get_fetch_recurse_config(submodule, spf))
+               switch (get_fetch_recurse_config(task->sub, spf))
                {
                default:
                case RECURSE_SUBMODULES_DEFAULT:
                case RECURSE_SUBMODULES_ON_DEMAND:
-                       if (!submodule || !unsorted_string_list_lookup(&changed_submodule_names,
-                                                        submodule->name))
+                       if (!task->sub ||
+                           !string_list_lookup(
+                                       &spf->changed_submodule_names,
+                                       task->sub->name))
                                continue;
                        default_argv = "on-demand";
                        break;
@@ -1293,16 +1437,12 @@ static int get_next_submodule(struct child_process *cp,
                        continue;
                }
 
-               strbuf_repo_worktree_path(&submodule_path, spf->r, "%s", ce->name);
-               strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
-               strbuf_addf(&submodule_prefix, "%s%s/", spf->prefix, ce->name);
-               git_dir = read_gitfile(submodule_git_dir.buf);
-               if (!git_dir)
-                       git_dir = submodule_git_dir.buf;
-               if (is_directory(git_dir)) {
+               task->repo = get_submodule_repo_for(spf->r, task->sub);
+               if (task->repo) {
+                       struct strbuf submodule_prefix = STRBUF_INIT;
                        child_process_init(cp);
-                       cp->dir = strbuf_detach(&submodule_path, NULL);
-                       prepare_submodule_repo_env(&cp->env_array);
+                       cp->dir = task->repo->gitdir;
+                       prepare_submodule_repo_env_in_gitdir(&cp->env_array);
                        cp->git_cmd = 1;
                        if (!spf->quiet)
                                strbuf_addf(err, "Fetching submodule %s%s\n",
@@ -1311,17 +1451,66 @@ static int get_next_submodule(struct child_process *cp,
                        argv_array_pushv(&cp->args, spf->args.argv);
                        argv_array_push(&cp->args, default_argv);
                        argv_array_push(&cp->args, "--submodule-prefix");
+
+                       strbuf_addf(&submodule_prefix, "%s%s/",
+                                                      spf->prefix,
+                                                      task->sub->path);
                        argv_array_push(&cp->args, submodule_prefix.buf);
-                       ret = 1;
-               }
-               strbuf_release(&submodule_path);
-               strbuf_release(&submodule_git_dir);
-               strbuf_release(&submodule_prefix);
-               if (ret) {
+
                        spf->count++;
+                       *task_cb = task;
+
+                       strbuf_release(&submodule_prefix);
                        return 1;
+               } else {
+
+                       fetch_task_release(task);
+                       free(task);
+
+                       /*
+                        * An empty directory is normal,
+                        * the submodule is not initialized
+                        */
+                       if (S_ISGITLINK(ce->ce_mode) &&
+                           !is_empty_dir(ce->name)) {
+                               spf->result = 1;
+                               strbuf_addf(err,
+                                           _("Could not access submodule '%s'"),
+                                           ce->name);
+                       }
                }
        }
+
+       if (spf->oid_fetch_tasks_nr) {
+               struct fetch_task *task =
+                       spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr - 1];
+               struct strbuf submodule_prefix = STRBUF_INIT;
+               spf->oid_fetch_tasks_nr--;
+
+               strbuf_addf(&submodule_prefix, "%s%s/",
+                           spf->prefix, task->sub->path);
+
+               child_process_init(cp);
+               prepare_submodule_repo_env_in_gitdir(&cp->env_array);
+               cp->git_cmd = 1;
+               cp->dir = task->repo->gitdir;
+
+               argv_array_init(&cp->args);
+               argv_array_pushv(&cp->args, spf->args.argv);
+               argv_array_push(&cp->args, "on-demand");
+               argv_array_push(&cp->args, "--submodule-prefix");
+               argv_array_push(&cp->args, submodule_prefix.buf);
+
+               /* NEEDSWORK: have get_default_remote from submodule--helper */
+               argv_array_push(&cp->args, "origin");
+               oid_array_for_each_unique(task->commits,
+                                         append_oid_to_argv, &cp->args);
+
+               *task_cb = task;
+               strbuf_release(&submodule_prefix);
+               return 1;
+       }
+
        return 0;
 }
 
@@ -1329,20 +1518,66 @@ static int fetch_start_failure(struct strbuf *err,
                               void *cb, void *task_cb)
 {
        struct submodule_parallel_fetch *spf = cb;
+       struct fetch_task *task = task_cb;
 
        spf->result = 1;
 
+       fetch_task_release(task);
        return 0;
 }
 
+static int commit_missing_in_sub(const struct object_id *oid, void *data)
+{
+       struct repository *subrepo = data;
+
+       enum object_type type = oid_object_info(subrepo, oid, NULL);
+
+       return type != OBJ_COMMIT;
+}
+
 static int fetch_finish(int retvalue, struct strbuf *err,
                        void *cb, void *task_cb)
 {
        struct submodule_parallel_fetch *spf = cb;
+       struct fetch_task *task = task_cb;
+
+       struct string_list_item *it;
+       struct oid_array *commits;
 
        if (retvalue)
                spf->result = 1;
 
+       if (!task || !task->sub)
+               BUG("callback cookie bogus");
+
+       /* Is this the second time we process this submodule? */
+       if (task->commits)
+               goto out;
+
+       it = string_list_lookup(&spf->changed_submodule_names, task->sub->name);
+       if (!it)
+               /* Could be an unchanged submodule, not contained in the list */
+               goto out;
+
+       commits = it->util;
+       oid_array_filter(commits,
+                        commit_missing_in_sub,
+                        task->repo);
+
+       /* Are there commits we want, but do not exist? */
+       if (commits->nr) {
+               task->commits = commits;
+               ALLOC_GROW(spf->oid_fetch_tasks,
+                          spf->oid_fetch_tasks_nr + 1,
+                          spf->oid_fetch_tasks_alloc);
+               spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr] = task;
+               spf->oid_fetch_tasks_nr++;
+               return 0;
+       }
+
+out:
+       fetch_task_release(task);
+
        return 0;
 }
 
@@ -1373,7 +1608,8 @@ int fetch_populated_submodules(struct repository *r,
        argv_array_push(&spf.args, "--recurse-submodules-default");
        /* default value, "--submodule-prefix" and its value are added later */
 
-       calculate_changed_submodule_paths(r);
+       calculate_changed_submodule_paths(r, &spf.changed_submodule_names);
+       string_list_sort(&spf.changed_submodule_names);
        run_processes_parallel(max_parallel_jobs,
                               get_next_submodule,
                               fetch_start_failure,
@@ -1382,7 +1618,7 @@ int fetch_populated_submodules(struct repository *r,
 
        argv_array_clear(&spf.args);
 out:
-       string_list_clear(&changed_submodule_names, 1);
+       free_submodules_oids(&spf.changed_submodule_names);
        return spf.result;
 }
 
@@ -1561,6 +1797,18 @@ out:
        return ret;
 }
 
+void submodule_unset_core_worktree(const struct submodule *sub)
+{
+       char *config_path = xstrfmt("%s/modules/%s/config",
+                                   get_git_common_dir(), sub->name);
+
+       if (git_config_set_in_file_gently(config_path, "core.worktree", NULL))
+               warning(_("Could not unset core.worktree setting in submodule '%s'"),
+                         sub->path);
+
+       free(config_path);
+}
+
 static const char *get_super_prefix_or_empty(void)
 {
        const char *s = get_super_prefix();
@@ -1726,6 +1974,8 @@ int submodule_move_head(const char *path,
 
                        if (is_empty_dir(path))
                                rmdir_or_warn(path);
+
+                       submodule_unset_core_worktree(sub);
                }
        }
 out:
index a680214c01a5fda90a21af547389f6333388683c..9e18e9b80760ad1562682125bc90d08fb6e1c0d3 100644 (file)
@@ -131,6 +131,8 @@ int submodule_move_head(const char *path,
                        const char *new_head,
                        unsigned flags);
 
+void submodule_unset_core_worktree(const struct submodule *sub);
+
 /*
  * Prepare the "env_array" parameter of a "struct child_process" for executing
  * a submodule by clearing any repo-specific environment variables, but
index 5261e8cf499006c1d84fc42a3e96e4dee7f09ba1..69d458a24d531be606bb43d360a9020d530557c8 100644 (file)
@@ -221,7 +221,7 @@ int has_symlink_leading_path(const char *name, int len)
  */
 int check_leading_path(const char *name, int len)
 {
-    return threaded_check_leading_path(&default_cache, name, len);
+       return threaded_check_leading_path(&default_cache, name, len);
 }
 
 /*
index 28711cc508f9e5dfb2f2ef238be14926d56f9a12..25864ec88384850342f3e6122f82470424e49953 100644 (file)
--- a/t/README
+++ b/t/README
@@ -186,6 +186,22 @@ appropriately before running "make".
        this feature by setting the GIT_TEST_CHAIN_LINT environment
        variable to "1" or "0", respectively.
 
+--stress::
+--stress=<N>::
+       Run the test script repeatedly in multiple parallel jobs until
+       one of them fails.  Useful for reproducing rare failures in
+       flaky tests.  The number of parallel jobs is, in order of
+       precedence: <N>, or the value of the GIT_TEST_STRESS_LOAD
+       environment variable, or twice the number of available
+       processors (as shown by the 'getconf' utility), or 8.
+       Implies `--verbose -x --immediate` to get the most information
+       about the failure.  Note that the verbose output of each test
+       job is saved to 't/test-results/$TEST_NAME.stress-<nr>.out',
+       and only the output of the failed test job is shown on the
+       terminal.  The names of the trash directories get a
+       '.stress-<nr>' suffix, and the trash directory of the failed
+       test job is renamed to end with a '.stress-failed' suffix.
+
 You can also set the GIT_TEST_INSTALLED environment variable to
 the bindir of an existing git installation to test that installation.
 You still need to have built this git sandbox, from which various
@@ -358,6 +374,11 @@ GIT_TEST_MULTI_PACK_INDEX=<boolean>, when true, forces the multi-pack-
 index to be written after every 'git repack' command, and overrides the
 'core.multiPackIndex' setting to true.
 
+GIT_TEST_SIDEBAND_ALL=<boolean>, when true, overrides the
+'uploadpack.allowSidebandAll' setting to true, and when false, forces
+fetch-pack to not request sideband-all (even if the server advertises
+sideband-all).
+
 Naming Tests
 ------------
 
@@ -425,7 +446,8 @@ This test harness library does the following things:
  - Creates an empty test directory with an empty .git/objects database
    and chdir(2) into it.  This directory is 't/trash
    directory.$test_name_without_dotsh', with t/ subject to change by
-   the --root option documented above.
+   the --root option documented above, and a '.stress-<N>' suffix
+   appended by the --stress option.
 
  - Defines standard test helper functions for your scripts to
    use.  These functions are designed to make all scripts behave
index b45bdac688c40104dd42ef68a07690420d96c142..8037eef7777b4f65af44b3c8cc3e4b46d48259ae 100755 (executable)
@@ -35,6 +35,7 @@ while (<>) {
                chomp;
        }
 
+       /\bcp\s+-a/ and err 'cp -a is not portable';
        /\bsed\s+-i/ and err 'sed -i is not portable';
        /\becho\s+-[neE]/ and err 'echo with option is not portable (use printf)';
        /^\s*declare\s+/ and err 'arrays/declare not portable';
diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c
new file mode 100644 (file)
index 0000000..432233c
--- /dev/null
@@ -0,0 +1,61 @@
+#include "test-tool.h"
+#include "cache.h"
+
+#define NUM_SECONDS 3
+
+static inline void compute_hash(const struct git_hash_algo *algo, git_hash_ctx *ctx, uint8_t *final, const void *p, size_t len)
+{
+       algo->init_fn(ctx);
+       algo->update_fn(ctx, p, len);
+       algo->final_fn(final, ctx);
+}
+
+int cmd__hash_speed(int ac, const char **av)
+{
+       git_hash_ctx ctx;
+       unsigned char hash[GIT_MAX_RAWSZ];
+       clock_t initial, start, end;
+       unsigned bufsizes[] = { 64, 256, 1024, 8192, 16384 };
+       int i;
+       void *p;
+       const struct git_hash_algo *algo = NULL;
+
+       if (ac == 2) {
+               for (i = 1; i < GIT_HASH_NALGOS; i++) {
+                       if (!strcmp(av[1], hash_algos[i].name)) {
+                               algo = &hash_algos[i];
+                               break;
+                       }
+               }
+       }
+       if (!algo)
+               die("usage: test-tool hash-speed algo_name");
+
+       /* Use this as an offset to make overflow less likely. */
+       initial = clock();
+
+       printf("algo: %s\n", algo->name);
+
+       for (i = 0; i < ARRAY_SIZE(bufsizes); i++) {
+               unsigned long j, kb;
+               double kb_per_sec;
+               p = xcalloc(1, bufsizes[i]);
+               start = end = clock() - initial;
+               for (j = 0; ((end - start) / CLOCKS_PER_SEC) < NUM_SECONDS; j++) {
+                       compute_hash(algo, &ctx, hash, p, bufsizes[i]);
+
+                       /*
+                        * Only check elapsed time every 128 iterations to avoid
+                        * dominating the runtime with system calls.
+                        */
+                       if (!(j & 127))
+                               end = clock() - initial;
+               }
+               kb = j * bufsizes[i];
+               kb_per_sec = kb / (1024 * ((double)end - start) / CLOCKS_PER_SEC);
+               printf("size %u: %lu iters; %lu KiB; %0.2f KiB/s\n", bufsizes[i], j, kb, kb_per_sec);
+               free(p);
+       }
+
+       exit(0);
+}
diff --git a/t/helper/test-hash.c b/t/helper/test-hash.c
new file mode 100644 (file)
index 0000000..0a31de6
--- /dev/null
@@ -0,0 +1,58 @@
+#include "test-tool.h"
+#include "cache.h"
+
+int cmd_hash_impl(int ac, const char **av, int algo)
+{
+       git_hash_ctx ctx;
+       unsigned char hash[GIT_MAX_HEXSZ];
+       unsigned bufsz = 8192;
+       int binary = 0;
+       char *buffer;
+       const struct git_hash_algo *algop = &hash_algos[algo];
+
+       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");
+       }
+
+       algop->init_fn(&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-hash");
+                       this_sz += sz;
+                       cp += sz;
+                       room -= sz;
+               }
+               if (this_sz == 0)
+                       break;
+               algop->update_fn(&ctx, buffer, this_sz);
+       }
+       algop->final_fn(hash, &ctx);
+
+       if (binary)
+               fwrite(hash, 1, algop->rawsz, stdout);
+       else
+               puts(hash_to_hex_algop(hash, algop));
+       exit(0);
+}
index e9e0541276c50d1739b6d39f2c01ba8ecb782adc..799fc00aa15b1e4cfe9fe8a6d32ebc2063003cb4 100644 (file)
@@ -233,7 +233,7 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)
 {
        const char *msg = notnull(*argv++, "msg");
        const char *refname = notnull(*argv++, "refname");
-       const char *new_sha1_buf = notnull(*argv++, "old-sha1");
+       const char *new_sha1_buf = notnull(*argv++, "new-sha1");
        const char *old_sha1_buf = notnull(*argv++, "old-sha1");
        unsigned int flags = arg_flags(*argv++, "flags");
        struct object_id old_oid;
index 6a84a53efbf6919c83d3f1fd73786acd92ee7abf..f7f861844560e0578eda989e39c4bd0572dcd5d6 100644 (file)
@@ -17,6 +17,11 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
 
        setup_git_env(gitdir);
 
+       memset(the_repository, 0, sizeof(*the_repository));
+
+       /* TODO: Needed for temporary hack in hashcmp, see 183a638b7da. */
+       repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
        if (repo_init(&r, gitdir, worktree))
                die("Couldn't init repo");
 
@@ -43,6 +48,11 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
 
        setup_git_env(gitdir);
 
+       memset(the_repository, 0, sizeof(*the_repository));
+
+       /* TODO: Needed for temporary hack in hashcmp, see 183a638b7da. */
+       repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
        if (repo_init(&r, gitdir, worktree))
                die("Couldn't init repo");
 
index 1ba0675c75f0d2dab281d054b577272cd45c39f9..d860c387c3846d69b7bd63a144ede2f93da60886 100644 (file)
@@ -3,55 +3,5 @@
 
 int cmd__sha1(int ac, const 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);
+       return cmd_hash_impl(ac, av, GIT_HASH_SHA1);
 }
diff --git a/t/helper/test-sha256.c b/t/helper/test-sha256.c
new file mode 100644 (file)
index 0000000..0ac6a99
--- /dev/null
@@ -0,0 +1,7 @@
+#include "test-tool.h"
+#include "cache.h"
+
+int cmd__sha256(int ac, const char **av)
+{
+       return cmd_hash_impl(ac, av, GIT_HASH_SHA256);
+}
index 77ac5bc33f8eb635f78d8ba590c23bbbe4f29636..d013bccddaebd9c7fb0eb4b4c4e1be0643f82260 100644 (file)
@@ -14,7 +14,8 @@ X(two)
 X(three)
 #undef X
 
-int cmd__sigchain(int argc, const char **argv) {
+int cmd__sigchain(int argc, const char **argv)
+{
        sigchain_push(SIGTERM, one);
        sigchain_push(SIGTERM, two);
        sigchain_push(SIGTERM, three);
index a31e2a9bea60a8699cbe4b335a84113cc531f7ed..bc97929bbc3a9f03c069ea556575de4ef4397d23 100644 (file)
@@ -10,19 +10,21 @@ static void die_usage(int argc, const char **argv, const char *msg)
 
 int cmd__submodule_nested_repo_config(int argc, const char **argv)
 {
-       struct repository submodule;
+       struct repository subrepo;
+       const struct submodule *sub;
 
        if (argc < 3)
                die_usage(argc, argv, "Wrong number of arguments.");
 
        setup_git_directory();
 
-       if (repo_submodule_init(&submodule, the_repository, argv[1])) {
+       sub = submodule_from_path(the_repository, &null_oid, argv[1]);
+       if (repo_submodule_init(&subrepo, the_repository, sub)) {
                die_usage(argc, argv, "Submodule not found.");
        }
 
        /* Read the config of _child_ submodules. */
-       print_config_from_gitmodules(&submodule, argv[2]);
+       print_config_from_gitmodules(&subrepo, argv[2]);
 
        submodule_free(the_repository);
 
index bfb195b1a828a34988912e66f9e07f493588d5b4..5b137874e1d21166c92d00f540f6ecd68c18780a 100644 (file)
@@ -20,6 +20,7 @@ static struct test_cmd cmds[] = {
        { "example-decorate", cmd__example_decorate },
        { "genrandom", cmd__genrandom },
        { "hashmap", cmd__hashmap },
+       { "hash-speed", cmd__hash_speed },
        { "index-version", cmd__index_version },
        { "json-writer", cmd__json_writer },
        { "lazy-init-name-hash", cmd__lazy_init_name_hash },
@@ -42,6 +43,7 @@ static struct test_cmd cmds[] = {
        { "scrap-cache-tree", cmd__scrap_cache_tree },
        { "sha1", cmd__sha1 },
        { "sha1-array", cmd__sha1_array },
+       { "sha256", cmd__sha256 },
        { "sigchain", cmd__sigchain },
        { "strcmp-offset", cmd__strcmp_offset },
        { "string-list", cmd__string_list },
index 042f12464b2a17afeb37e01d90bf49ba33ce879a..ca5c88edb2ac8ae99090ec21e6b3d636bc03e96c 100644 (file)
@@ -16,6 +16,7 @@ int cmd__dump_untracked_cache(int argc, const char **argv);
 int cmd__example_decorate(int argc, const char **argv);
 int cmd__genrandom(int argc, const char **argv);
 int cmd__hashmap(int argc, const char **argv);
+int cmd__hash_speed(int argc, const char **argv);
 int cmd__index_version(int argc, const char **argv);
 int cmd__json_writer(int argc, const char **argv);
 int cmd__lazy_init_name_hash(int argc, const char **argv);
@@ -38,6 +39,7 @@ int cmd__run_command(int argc, const char **argv);
 int cmd__scrap_cache_tree(int argc, const char **argv);
 int cmd__sha1(int argc, const char **argv);
 int cmd__sha1_array(int argc, const char **argv);
+int cmd__sha256(int argc, const char **argv);
 int cmd__sigchain(int argc, const char **argv);
 int cmd__strcmp_offset(int argc, const char **argv);
 int cmd__string_list(int argc, const char **argv);
@@ -51,4 +53,6 @@ int cmd__windows_named_pipe(int argc, const char **argv);
 #endif
 int cmd__write_cache(int argc, const char **argv);
 
+int cmd_hash_impl(int ac, const char **av, int algo);
+
 #endif
index f98de95c15b14aa4030c65d26e6270dc06088372..79db3b7ae513c01b07422ed1a8d95f9f5b285cb5 100644 (file)
@@ -28,7 +28,7 @@ then
        test_skip_or_die $GIT_TEST_GIT_DAEMON "file system does not support FIFOs"
 fi
 
-LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-${this_test#t}}
+test_set_port LIB_GIT_DAEMON_PORT
 
 GIT_DAEMON_PID=
 GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo
@@ -54,19 +54,11 @@ start_git_daemon() {
                "$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
                >&3 2>git_daemon_output &
        GIT_DAEMON_PID=$!
-       >daemon.log
        {
                read -r line <&7
-               printf "%s\n" "$line"
-               printf >&4 "%s\n" "$line"
-               (
-                       while read -r line <&7
-                       do
-                               printf "%s\n" "$line"
-                               printf >&4 "%s\n" "$line"
-                       done
-               ) &
-       } 7<git_daemon_output >>"$TRASH_DIRECTORY/daemon.log" &&
+               printf "%s\n" "$line" >&4
+               cat <&7 >&4 &
+       } 7<git_daemon_output &&
 
        # Check expected output
        if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble"
index c27599474cf2f272b53e2e76997e5e38af0fe647..b3be3ba011a71ce11f11901c8472cb25ec21133a 100644 (file)
@@ -53,14 +53,7 @@ time_in_seconds () {
        (cd / && "$PYTHON_PATH" -c 'import time; print(int(time.time()))')
 }
 
-# Try to pick a unique port: guess a large number, then hope
-# no more than one of each test is running.
-#
-# This does not handle the case where somebody else is running the
-# same tests and has chosen the same ports.
-testid=${this_test#t}
-git_p4_test_start=9800
-P4DPORT=$((10669 + ($testid - $git_p4_test_start)))
+test_set_port P4DPORT
 
 P4PORT=localhost:$P4DPORT
 P4CLIENT=client
index a8130f9119d629462efb6b52f91890c0352e4e85..f3b478c307c99397fb180fb3d5688e1d6f257904 100644 (file)
@@ -13,6 +13,7 @@ fi
 GIT_DIR=$PWD/.git
 GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
 SVN_TREE=$GIT_SVN_DIR/svn-tree
+test_set_port SVNSERVE_PORT
 
 svn >/dev/null 2>&1
 if test $? -ne 1
@@ -119,7 +120,6 @@ require_svnserve () {
 }
 
 start_svnserve () {
-       SVNSERVE_PORT=${SVNSERVE_PORT-${this_test#t}}
        svnserve --listen-port $SVNSERVE_PORT \
                 --root "$rawsvnrepo" \
                 --listen-once \
index a8729f82325ee7fb9350c42553e11205e6720928..e465116ef950404521ed2e119ef7cf398bf9da81 100644 (file)
@@ -82,7 +82,7 @@ case $(uname) in
 esac
 
 LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"}
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-${this_test#t}}
+test_set_port LIB_HTTPD_PORT
 
 TEST_PATH="$TEST_DIRECTORY"/lib-httpd
 HTTPD_ROOT_PATH="$PWD"/httpd
index 581c010d8fc4c1184ba17b63b940aaac30d0b0f4..5d63ed90c51cf215dd24bf634f47413c2fa27368 100644 (file)
@@ -78,6 +78,7 @@ PassEnv GNUPGHOME
 PassEnv ASAN_OPTIONS
 PassEnv GIT_TRACE
 PassEnv GIT_CONFIG_NOSYSTEM
+PassEnv GIT_TEST_SIDEBAND_ALL
 
 SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0
 
@@ -115,6 +116,7 @@ Alias /auth/dumb/ www/auth/dumb/
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
        SetEnv GIT_HTTP_EXPORT_ALL
 </LocationMatch>
+ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/
 ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 ScriptAlias /broken_smart/ broken-smart-http.sh/
 ScriptAlias /error/ error.sh/
index 016391723c09491e53ebf4e0b69972783c7bc491..5b56b23166bb3dea2e89cea38a19eb5698dfff53 100755 (executable)
@@ -235,7 +235,7 @@ reset_work_tree_to_interested () {
        then
                mkdir -p submodule_update/.git/modules/sub1/modules &&
                cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2
-               GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree
+               # core.worktree is unset for sub2 as it is not checked out
        fi &&
        # indicate we are interested in the submodule:
        git -C submodule_update config submodule.sub1.url "bogus" &&
@@ -709,7 +709,8 @@ test_submodule_recursing_with_args_common() {
                        git branch -t remove_sub1 origin/remove_sub1 &&
                        $command remove_sub1 &&
                        test_superproject_content origin/remove_sub1 &&
-                       ! test -e sub1
+                       ! test -e sub1 &&
+                       test_must_fail git config -f .git/modules/sub1/config core.worktree
                )
        '
        # ... absorbing a .git directory along the way.
index 22499bce5f50637e574961f744b567b974a4d3d0..71e63d8b509d34fa4b860bac9ba1531e674799d7 100755 (executable)
@@ -322,4 +322,24 @@ test_expect_success 'bare repository: test info/attributes' '
        )
 '
 
+test_expect_success 'binary macro expanded by -a' '
+       echo "file binary" >.gitattributes &&
+       cat >expect <<-\EOF &&
+       file: binary: set
+       file: diff: unset
+       file: merge: unset
+       file: text: unset
+       EOF
+       git check-attr -a file >actual &&
+       test_cmp expect actual
+'
+
+
+test_expect_success 'query binary macro directly' '
+       echo "file binary" >.gitattributes &&
+       echo file: binary: set >expect &&
+       git check-attr binary file >actual &&
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t0015-hash.sh b/t/t0015-hash.sh
new file mode 100755 (executable)
index 0000000..291e906
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='test basic hash implementation'
+. ./test-lib.sh
+
+
+test_expect_success 'test basic SHA-1 hash values' '
+       test-tool sha1 </dev/null >actual &&
+       grep da39a3ee5e6b4b0d3255bfef95601890afd80709 actual &&
+       printf "a" | test-tool sha1 >actual &&
+       grep 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 actual &&
+       printf "abc" | test-tool sha1 >actual &&
+       grep a9993e364706816aba3e25717850c26c9cd0d89d actual &&
+       printf "message digest" | test-tool sha1 >actual &&
+       grep c12252ceda8be8994d5fa0290a47231c1d16aae3 actual &&
+       printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha1 >actual &&
+       grep 32d10c7b8cf96570ca04ce37f2a19d84240d3a89 actual &&
+       perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \
+               test-tool sha1 >actual &&
+       grep 34aa973cd4c4daa4f61eeb2bdbad27316534016f actual &&
+       printf "blob 0\0" | test-tool sha1 >actual &&
+       grep e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 actual &&
+       printf "blob 3\0abc" | test-tool sha1 >actual &&
+       grep f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f actual &&
+       printf "tree 0\0" | test-tool sha1 >actual &&
+       grep 4b825dc642cb6eb9a060e54bf8d69288fbee4904 actual
+'
+
+test_expect_success 'test basic SHA-256 hash values' '
+       test-tool sha256 </dev/null >actual &&
+       grep e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 actual &&
+       printf "a" | test-tool sha256 >actual &&
+       grep ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb actual &&
+       printf "abc" | test-tool sha256 >actual &&
+       grep ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad actual &&
+       printf "message digest" | test-tool sha256 >actual &&
+       grep f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650 actual &&
+       printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha256 >actual &&
+       grep 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 actual &&
+       # Try to exercise the chunking code by turning autoflush on.
+       perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \
+               test-tool sha256 >actual &&
+       grep cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 actual &&
+       perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" | \
+               test-tool sha256 >actual &&
+       grep e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35 actual &&
+       printf "blob 0\0" | test-tool sha256 >actual &&
+       grep 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 actual &&
+       printf "blob 3\0abc" | test-tool sha256 >actual &&
+       grep c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6 actual &&
+       printf "tree 0\0" | test-tool sha256 >actual &&
+       grep 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321 actual
+'
+
+test_done
index 9d9e02a21112197a2a57a2877db24951d41a2996..e13363ade5cf500e6f57677eb961c5ca01197a12 100755 (executable)
@@ -27,4 +27,13 @@ test_expect_success 'renormalize CRLF in repo' '
        test_cmp expect actual
 '
 
+test_expect_success 'ignore-errors not mistaken for renormalize' '
+       git reset --hard &&
+       echo "*.txt text=auto" >.gitattributes &&
+       git ls-files --eol >expect &&
+       git add --ignore-errors "*.txt" &&
+       git ls-files --eol >actual &&
+       test_cmp expect actual
+'
+
 test_done
index beb5927f77f7f21456eef2835ee67402e487a899..3587e454f19d9bf71f2b02c9189d4715ed216fd9 100755 (executable)
@@ -293,9 +293,9 @@ checkout_files () {
        do
                rm crlf_false_attr__$f.txt &&
                if test -z "$ceol"; then
-                       git checkout crlf_false_attr__$f.txt
+                       git checkout -- crlf_false_attr__$f.txt
                else
-                       git -c core.eol=$ceol checkout crlf_false_attr__$f.txt
+                       git -c core.eol=$ceol checkout -- crlf_false_attr__$f.txt
                fi
        done
 
index 7e87b5a200a3f6483ea2e243fa0c497205ce4e61..e58ecbfc44037fa1d38707b30545e803e1401b4f 100755 (executable)
@@ -11,9 +11,12 @@ test_expect_success 'setup test files' '
 
        text="hallo there!\ncan you read me?" &&
        echo "*.utf16 text working-tree-encoding=utf-16" >.gitattributes &&
+       echo "*.utf16lebom text working-tree-encoding=UTF-16LE-BOM" >>.gitattributes &&
        printf "$text" >test.utf8.raw &&
        printf "$text" | iconv -f UTF-8 -t UTF-16 >test.utf16.raw &&
        printf "$text" | iconv -f UTF-8 -t UTF-32 >test.utf32.raw &&
+       printf "\377\376"                         >test.utf16lebom.raw &&
+       printf "$text" | iconv -f UTF-8 -t UTF-32LE >>test.utf16lebom.raw &&
 
        # Line ending tests
        printf "one\ntwo\nthree\n" >lf.utf8.raw &&
@@ -32,7 +35,8 @@ test_expect_success 'setup test files' '
        # Add only UTF-16 file, we will add the UTF-32 file later
        cp test.utf16.raw test.utf16 &&
        cp test.utf32.raw test.utf32 &&
-       git add .gitattributes test.utf16 &&
+       cp test.utf16lebom.raw test.utf16lebom &&
+       git add .gitattributes test.utf16 test.utf16lebom &&
        git commit -m initial
 '
 
@@ -51,6 +55,12 @@ test_expect_success 're-encode to UTF-16 on checkout' '
        test_cmp_bin test.utf16.raw test.utf16
 '
 
+test_expect_success 're-encode to UTF-16-LE-BOM on checkout' '
+       rm test.utf16lebom &&
+       git checkout test.utf16lebom &&
+       test_cmp_bin test.utf16lebom.raw test.utf16lebom
+'
+
 test_expect_success 'check $GIT_DIR/info/attributes support' '
        test_when_finished "rm -f test.utf32.git" &&
        test_when_finished "git reset --hard HEAD" &&
index 5ce47e8af51d63c7bd294e6f7582f10e4891566d..0c24a0f9a377103f49fa7cb749a5657058da3490 100755 (executable)
@@ -430,9 +430,15 @@ test_expect_success '-c with changed comment char' '
 test_expect_success '-c with comment char defined in .git/config' '
        test_config core.commentchar = &&
        printf "= foo\n" >expect &&
-       printf "foo" | (
-               mkdir sub && cd sub && git stripspace -c
-       ) >actual &&
+       rm -fr sub &&
+       mkdir sub &&
+       printf "foo" | git -C sub stripspace -c >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '-c outside git repository' '
+       printf "# foo\n" >expect &&
+       printf "foo" | nongit git stripspace -c >actual &&
        test_cmp expect actual
 '
 
index f5b10861c498e435155f6aec5dec6f27c82aa51b..b8f366c4420f47d913b1d830037d846e4e443074 100755 (executable)
@@ -227,7 +227,7 @@ EOF
 test_expect_success 'detect possible typos' '
        test_must_fail test-tool parse-options -boolean >output 2>output.err &&
        test_must_be_empty output &&
-       test_cmp typo.err output.err
+       test_i18ncmp typo.err output.err
 '
 
 cat >typo.err <<\EOF
@@ -237,7 +237,7 @@ EOF
 test_expect_success 'detect possible typos' '
        test_must_fail test-tool parse-options -ambiguous >output 2>output.err &&
        test_must_be_empty output &&
-       test_cmp typo.err output.err
+       test_i18ncmp typo.err output.err
 '
 
 test_expect_success 'keep some options as arguments' '
index 96bf6d6a7d0145334666fcd15373bad6ac66fd67..9c7604dcabec86776c3bb25598e39dac79118765 100755 (executable)
@@ -31,7 +31,15 @@ test_expect_success 'run_command can run a command' '
        test_must_be_empty err
 '
 
-test_expect_success 'run_command is restricted to PATH' '
+
+test_lazy_prereq RUNS_COMMANDS_FROM_PWD '
+       write_script runs-commands-from-pwd <<-\EOF &&
+       true
+       EOF
+       runs-commands-from-pwd >/dev/null 2>&1
+'
+
+test_expect_success !RUNS_COMMANDS_FROM_PWD 'run_command is restricted to PATH' '
        write_script should-not-run <<-\EOF &&
        echo yikes
        EOF
@@ -191,4 +199,14 @@ test_expect_success 'GIT_TRACE with environment variables' '
        )
 '
 
+test_expect_success MINGW 'verify curlies are quoted properly' '
+       : force the rev-parse through the MSYS2 Bash &&
+       git -c alias.r="!git rev-parse" r -- a{b}c >actual &&
+       cat >expect <<-\EOF &&
+       --
+       a{b}c
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index ba3887f178b03a71b1ee1d2149429e2ee76814a6..bce02788e6e1b03b87911fb13594e0cf6f80fdab 100755 (executable)
@@ -349,7 +349,7 @@ test_expect_success 'rev-list stops traversal at promisor commit, tree, and blob
        grep $(git -C repo rev-parse bar) out  # sanity check that some walking was done
 '
 
-test_expect_success 'rev-list accepts missing and promised objects on command line' '
+test_expect_success 'rev-list dies for missing objects on cmd line' '
        rm -rf repo &&
        test_create_repo repo &&
        test_commit -C repo foo &&
@@ -366,7 +366,19 @@ test_expect_success 'rev-list accepts missing and promised objects on command li
 
        git -C repo config core.repositoryformatversion 1 &&
        git -C repo config extensions.partialclone "arbitrary string" &&
-       git -C repo rev-list --exclude-promisor-objects --objects "$COMMIT" "$TREE" "$BLOB"
+
+       for OBJ in "$COMMIT" "$TREE" "$BLOB"; do
+               test_must_fail git -C repo rev-list --objects \
+                       --exclude-promisor-objects "$OBJ" &&
+               test_must_fail git -C repo rev-list --objects-edge-aggressive \
+                       --exclude-promisor-objects "$OBJ" &&
+
+               # Do not die or crash when --ignore-missing is passed.
+               git -C repo rev-list --ignore-missing --objects \
+                       --exclude-promisor-objects "$OBJ" &&
+               git -C repo rev-list --ignore-missing --objects-edge-aggressive \
+                       --exclude-promisor-objects "$OBJ"
+       done
 '
 
 test_expect_success 'gc repacks promisor objects separately from non-promisor objects' '
@@ -480,7 +492,6 @@ test_expect_success 'gc stops traversal when a missing but promised object is re
        ! grep "$TREE_HASH" out
 '
 
-LIB_HTTPD_PORT=12345  # default port, 410, cannot be used as non-root
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
index 3e053532eb8df817e216532c807eba0ba549e8c3..ae8a448e34356034a88fcf0799ef2f53e55f73b8 100755 (executable)
@@ -20,12 +20,12 @@ check_have () {
 }
 
 check_fsck () {
-       output=$(git fsck --full)
+       git fsck --full >fsck.output
        case "$1" in
        '')
-               test -z "$output" ;;
+               test_must_be_empty fsck.output ;;
        *)
-               echo "$output" | grep "$1" ;;
+               test_i18ngrep "$1" fsck.output ;;
        esac
 }
 
index e20e8fa8301607b94cff7c42e83545f072d5191f..2e5e979336c5c67bf59667bbf5e0183b632ea2da 100755 (executable)
@@ -70,7 +70,7 @@ test_expect_success 'object with bad sha1' '
 
        test_must_fail git fsck 2>out &&
        cat out &&
-       grep "$sha.*corrupt" out
+       test_i18ngrep "$sha.*corrupt" out
 '
 
 test_expect_success 'branch pointing to non-commit' '
@@ -78,7 +78,7 @@ test_expect_success 'branch pointing to non-commit' '
        test_when_finished "git update-ref -d refs/heads/invalid" &&
        test_must_fail git fsck 2>out &&
        cat out &&
-       grep "not a commit" out
+       test_i18ngrep "not a commit" out
 '
 
 test_expect_success 'HEAD link pointing at a funny object' '
@@ -88,7 +88,7 @@ test_expect_success 'HEAD link pointing at a funny object' '
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail env GIT_DIR=.git git fsck 2>out &&
        cat out &&
-       grep "detached HEAD points" out
+       test_i18ngrep "detached HEAD points" out
 '
 
 test_expect_success 'HEAD link pointing at a funny place' '
@@ -98,7 +98,7 @@ test_expect_success 'HEAD link pointing at a funny place' '
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail env GIT_DIR=.git git fsck 2>out &&
        cat out &&
-       grep "HEAD points to something strange" out
+       test_i18ngrep "HEAD points to something strange" out
 '
 
 test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
@@ -109,7 +109,7 @@ test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
        echo $ZERO_OID >.git/HEAD &&
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail git -C wt fsck 2>out &&
-       grep "main-worktree/HEAD: detached HEAD points" out
+       test_i18ngrep "main-worktree/HEAD: detached HEAD points" out
 '
 
 test_expect_success 'other worktree HEAD link pointing at a funny object' '
@@ -117,7 +117,7 @@ test_expect_success 'other worktree HEAD link pointing at a funny object' '
        git worktree add other &&
        echo $ZERO_OID >.git/worktrees/other/HEAD &&
        test_must_fail git fsck 2>out &&
-       grep "worktrees/other/HEAD: detached HEAD points" out
+       test_i18ngrep "worktrees/other/HEAD: detached HEAD points" out
 '
 
 test_expect_success 'other worktree HEAD link pointing at missing object' '
@@ -125,7 +125,7 @@ test_expect_success 'other worktree HEAD link pointing at missing object' '
        git worktree add other &&
        echo "Contents missing from repo" | git hash-object --stdin >.git/worktrees/other/HEAD &&
        test_must_fail git fsck 2>out &&
-       grep "worktrees/other/HEAD: invalid sha1 pointer" out
+       test_i18ngrep "worktrees/other/HEAD: invalid sha1 pointer" out
 '
 
 test_expect_success 'other worktree HEAD link pointing at a funny place' '
@@ -133,7 +133,7 @@ test_expect_success 'other worktree HEAD link pointing at a funny place' '
        git worktree add other &&
        echo "ref: refs/funny/place" >.git/worktrees/other/HEAD &&
        test_must_fail git fsck 2>out &&
-       grep "worktrees/other/HEAD points to something strange" out
+       test_i18ngrep "worktrees/other/HEAD points to something strange" out
 '
 
 test_expect_success 'email without @ is okay' '
@@ -157,7 +157,7 @@ test_expect_success 'email with embedded > is not okay' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
-       grep "error in commit $new" out
+       test_i18ngrep "error in commit $new" out
 '
 
 test_expect_success 'missing < email delimiter is reported nicely' '
@@ -169,7 +169,7 @@ test_expect_success 'missing < email delimiter is reported nicely' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
-       grep "error in commit $new.* - bad name" out
+       test_i18ngrep "error in commit $new.* - bad name" out
 '
 
 test_expect_success 'missing email is reported nicely' '
@@ -181,7 +181,7 @@ test_expect_success 'missing email is reported nicely' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
-       grep "error in commit $new.* - missing email" out
+       test_i18ngrep "error in commit $new.* - missing email" out
 '
 
 test_expect_success '> in name is reported' '
@@ -193,7 +193,7 @@ test_expect_success '> in name is reported' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
-       grep "error in commit $new" out
+       test_i18ngrep "error in commit $new" out
 '
 
 # date is 2^64 + 1
@@ -207,7 +207,7 @@ test_expect_success 'integer overflow in timestamps is reported' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
-       grep "error in commit $new.*integer overflow" out
+       test_i18ngrep "error in commit $new.*integer overflow" out
 '
 
 test_expect_success 'commit with NUL in header' '
@@ -219,7 +219,7 @@ test_expect_success 'commit with NUL in header' '
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        test_must_fail git fsck 2>out &&
        cat out &&
-       grep "error in commit $new.*unterminated header: NUL at offset" out
+       test_i18ngrep "error in commit $new.*unterminated header: NUL at offset" out
 '
 
 test_expect_success 'tree object with duplicate entries' '
@@ -240,7 +240,7 @@ test_expect_success 'tree object with duplicate entries' '
                git hash-object -w -t tree --stdin
        ) &&
        test_must_fail git fsck 2>out &&
-       grep "error in tree .*contains duplicate file entries" out
+       test_i18ngrep "error in tree .*contains duplicate file entries" out
 '
 
 test_expect_success 'unparseable tree object' '
@@ -294,7 +294,7 @@ test_expect_success 'tag pointing to nonexistent' '
        test_when_finished "git update-ref -d refs/tags/invalid" &&
        test_must_fail git fsck --tags >out &&
        cat out &&
-       grep "broken link" out
+       test_i18ngrep "broken link" out
 '
 
 test_expect_success 'tag pointing to something else than its type' '
@@ -336,7 +336,7 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '
        warning in tag $tag: badTagName: invalid '\''tag'\'' name: wrong name format
        warning in tag $tag: missingTaggerEntry: invalid format - expected '\''tagger'\'' line
        EOF
-       test_cmp expect out
+       test_i18ncmp expect out
 '
 
 test_expect_success 'tag with bad tagger' '
@@ -355,7 +355,7 @@ test_expect_success 'tag with bad tagger' '
        echo $tag >.git/refs/tags/wrong &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags 2>out &&
-       grep "error in tag .*: invalid author/committer" out
+       test_i18ngrep "error in tag .*: invalid author/committer" out
 '
 
 test_expect_success 'tag with NUL in header' '
@@ -375,7 +375,7 @@ test_expect_success 'tag with NUL in header' '
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags 2>out &&
        cat out &&
-       grep "error in tag $tag.*unterminated header: NUL at offset" out
+       test_i18ngrep "error in tag $tag.*unterminated header: NUL at offset" out
 '
 
 test_expect_success 'cleaned up' '
@@ -431,7 +431,7 @@ test_expect_success 'fsck notices blob entry pointing to null sha1' '
               git hash-object -w --stdin -t tree) &&
          git fsck 2>out &&
          cat out &&
-         grep "warning.*null sha1" out
+         test_i18ngrep "warning.*null sha1" out
        )
 '
 
@@ -442,7 +442,7 @@ test_expect_success 'fsck notices submodule entry pointing to null sha1' '
               git hash-object -w --stdin -t tree) &&
          git fsck 2>out &&
          cat out &&
-         grep "warning.*null sha1" out
+         test_i18ngrep "warning.*null sha1" out
        )
 '
 
@@ -463,7 +463,7 @@ while read name path pretty; do
                        bad_tree=$(git mktree <bad) &&
                        git fsck 2>out &&
                        cat out &&
-                       grep "warning.*tree $bad_tree" out
+                       test_i18ngrep "warning.*tree $bad_tree" out
                )'
        done <<-\EOF
        100644 blob
@@ -509,9 +509,9 @@ test_expect_success 'NUL in commit' '
                git branch bad $(cat name) &&
 
                test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
-               grep nulInCommit warn.1 &&
+               test_i18ngrep nulInCommit warn.1 &&
                git fsck 2>warn.2 &&
-               grep nulInCommit warn.2
+               test_i18ngrep nulInCommit warn.2
        )
 '
 
@@ -629,7 +629,7 @@ test_expect_success 'fsck --name-objects' '
                remove_object $(git rev-parse julius:caesar.t) &&
                test_must_fail git fsck --name-objects >out &&
                tree=$(git rev-parse --verify julius:) &&
-               egrep "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out
+               test_i18ngrep -E "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out
        )
 '
 
@@ -640,7 +640,7 @@ test_expect_success 'alternate objects are correctly blamed' '
        mkdir alt.git/objects/12 &&
        >alt.git/objects/12/34567890123456789012345678901234567890 &&
        test_must_fail git fsck >out 2>&1 &&
-       grep alt.git out
+       test_i18ngrep alt.git out
 '
 
 test_expect_success 'fsck errors in packed objects' '
@@ -659,8 +659,8 @@ test_expect_success 'fsck errors in packed objects' '
        remove_object $one &&
        remove_object $two &&
        test_must_fail git fsck 2>out &&
-       grep "error in commit $one.* - bad name" out &&
-       grep "error in commit $two.* - bad name" out &&
+       test_i18ngrep "error in commit $one.* - bad name" out &&
+       test_i18ngrep "error in commit $two.* - bad name" out &&
        ! grep corrupt out
 '
 
@@ -760,7 +760,7 @@ test_expect_success 'fsck notices dangling objects' '
                git fsck >actual &&
                # the output order is non-deterministic, as it comes from a hash
                sort <actual >actual.sorted &&
-               test_cmp expect actual.sorted
+               test_i18ncmp expect actual.sorted
        )
 '
 
@@ -808,7 +808,7 @@ test_expect_success 'detect corrupt index file in fsck' '
        test_when_finished "mv .git/index.backup .git/index" &&
        corrupt_index_checksum &&
        test_must_fail git fsck --cache 2>errors &&
-       grep "bad index file" errors
+       test_i18ngrep "bad index file" errors
 '
 
 test_done
index e4d5b56014822ff760d3eab6d7c798f8d932ca45..c19fb500cb225273fce730c656fea00a3ada624d 100755 (executable)
@@ -388,4 +388,14 @@ test_expect_success C_LOCALE_OUTPUT 'ambiguous commits are printed by type first
        done
 '
 
+test_expect_success 'cat-file --batch and --batch-check show ambiguous' '
+       echo "0000 ambiguous" >expect &&
+       echo 0000 | git cat-file --batch-check >actual 2>err &&
+       test_cmp expect actual &&
+       test_i18ngrep hint: err &&
+       echo 0000 | git cat-file --batch >actual 2>err &&
+       test_cmp expect actual &&
+       test_i18ngrep hint: err
+'
+
 test_done
index 2131fb2a5682c50faf64e6066f03080702168b94..c5014ad9a63f2451f84db5076f902a0132d318c1 100755 (executable)
@@ -198,4 +198,13 @@ test_expect_success 'checkout -B to the current branch works' '
        test_dirty_mergeable
 '
 
+test_expect_success 'checkout -b after clone --no-checkout does a checkout of HEAD' '
+       git init src &&
+       test_commit -C src a &&
+       rev="$(git -C src rev-parse HEAD)" &&
+       git clone --no-checkout src dest &&
+       git -C dest checkout "$rev" -b branch &&
+       test_path_is_file dest/a.t
+'
+
 test_done
index 69b6774d10a9d90790316e9feb0f718593465c76..fa0718c730c322bfe5f9e6642c4136a7533be5dc 100755 (executable)
@@ -278,4 +278,35 @@ test_expect_success 'loosely defined local base branch is reported correctly' '
        test_cmp expect actual
 '
 
+test_expect_success 'reject when arg could be part of dwim branch' '
+       git remote add foo file://non-existent-place &&
+       git update-ref refs/remotes/foo/dwim-arg HEAD &&
+       echo foo >dwim-arg &&
+       git add dwim-arg &&
+       echo bar >dwim-arg &&
+       test_must_fail git checkout dwim-arg &&
+       test_must_fail git rev-parse refs/heads/dwim-arg -- &&
+       grep bar dwim-arg
+'
+
+test_expect_success 'disambiguate dwim branch and checkout path (1)' '
+       git update-ref refs/remotes/foo/dwim-arg1 HEAD &&
+       echo foo >dwim-arg1 &&
+       git add dwim-arg1 &&
+       echo bar >dwim-arg1 &&
+       git checkout -- dwim-arg1 &&
+       test_must_fail git rev-parse refs/heads/dwim-arg1 -- &&
+       grep foo dwim-arg1
+'
+
+test_expect_success 'disambiguate dwim branch and checkout path (2)' '
+       git update-ref refs/remotes/foo/dwim-arg2 HEAD &&
+       echo foo >dwim-arg2 &&
+       git add dwim-arg2 &&
+       echo bar >dwim-arg2 &&
+       git checkout dwim-arg2 -- &&
+       git rev-parse refs/heads/dwim-arg2 -- &&
+       grep bar dwim-arg2
+'
+
 test_done
index 33c033773367a135d4cb7eb23f9e9d3131197174..939d18d7286c1be1e58a698e9164fda8e24c654a 100755 (executable)
@@ -112,6 +112,26 @@ test_expect_success 'move locked worktree (force)' '
        git worktree move --force --force flump ploof
 '
 
+test_expect_success 'move a repo with uninitialized submodule' '
+       git init withsub &&
+       (
+               cd withsub &&
+               test_commit initial &&
+               git submodule add "$PWD"/.git sub &&
+               git commit -m withsub &&
+               git worktree add second HEAD &&
+               git worktree move second third
+       )
+'
+
+test_expect_success 'not move a repo with initialized submodule' '
+       (
+               cd withsub &&
+               git -C third submodule update &&
+               test_must_fail git worktree move third forth
+       )
+'
+
 test_expect_success 'remove main worktree' '
        test_must_fail git worktree remove .
 '
@@ -185,4 +205,21 @@ test_expect_success 'remove cleans up .git/worktrees when empty' '
        )
 '
 
+test_expect_success 'remove a repo with uninitialized submodule' '
+       (
+               cd withsub &&
+               git worktree add to-remove HEAD &&
+               git worktree remove to-remove
+       )
+'
+
+test_expect_success 'not remove a repo with initialized submodule' '
+       (
+               cd withsub &&
+               git worktree add to-remove HEAD &&
+               git -C to-remove submodule update &&
+               test_must_fail git worktree remove to-remove
+       )
+'
+
 test_done
index 7a440e08d8257487ae2f47b159d11e37a5166df8..2b961745cc0aa302d3ac8ea72c4ad919de2e8bbf 100755 (executable)
@@ -156,6 +156,11 @@ test_expect_success 'rebase -i with exec of inexistent command' '
        ! grep "Maybe git-rebase is broken" actual
 '
 
+test_expect_success 'implicit interactive rebase does not invoke sequence editor' '
+       test_when_finished "git rebase --abort ||:" &&
+       GIT_SEQUENCE_EDITOR="echo bad >" git rebase -x"echo one" @^
+'
+
 test_expect_success 'no changes are a nop' '
        git checkout branch2 &&
        set_fake_editor &&
index f64b130cb805bbca8475f3c262693fa0faa2cafe..b393e1e9fee88bb9b3df6ecc8544dfa370086e41 100755 (executable)
@@ -17,14 +17,9 @@ test_expect_success 'setup' '
        git tag start
 '
 
-cat >expect <<\EOF
-Already applied: 0001 A
-Already applied: 0002 B
-Committed: 0003 Z
-EOF
-
 test_expect_success 'rebase -m' '
        git rebase -m master >report &&
+       >expect &&
        sed -n -e "/^Already applied: /p" \
                -e "/^Committed: /p" report >actual &&
        test_cmp expect actual
index 0210b2ac6f0709cce4bf7b656b3c61a751330e61..25aaacacfc6b7beb1e29b73678777a76c6af9f71 100755 (executable)
@@ -254,4 +254,18 @@ test_expect_success 'the todo command "break" works' '
        test_path_is_file execed
 '
 
+test_expect_success '--reschedule-failed-exec' '
+       test_when_finished "git rebase --abort" &&
+       test_must_fail git rebase -x false --reschedule-failed-exec HEAD^ &&
+       grep "^exec false" .git/rebase-merge/git-rebase-todo &&
+       git rebase --abort &&
+       test_must_fail git -c rebase.rescheduleFailedExec=true \
+               rebase -x false HEAD^ 2>err &&
+       grep "^exec false" .git/rebase-merge/git-rebase-todo &&
+       test_i18ngrep "has been rescheduled" err &&
+       git rebase --abort &&
+       test_must_fail git rebase -y false HEAD^ 2>err &&
+       test_i18ngrep "has been rescheduled" err
+'
+
 test_done
index 4c7494cc8f77a3ff92373cb130974825c4418ada..2d1094e4831a88205133558d2ca6ac5ac07209d1 100755 (executable)
@@ -53,41 +53,6 @@ create_expected_success_interactive () {
        EOF
 }
 
-create_expected_success_merge () {
-       cat >expected <<-EOF
-       $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
-       HEAD is now at $(git rev-parse --short feature-branch) third commit
-       First, rewinding head to replay your work on top of it...
-       Merging unrelated-onto-branch with HEAD~1
-       Merging:
-       $(git rev-parse --short unrelated-onto-branch) unrelated commit
-       $(git rev-parse --short feature-branch^) second commit
-       found 1 common ancestor:
-       $(git rev-parse --short feature-branch~2) initial commit
-       [detached HEAD $(git rev-parse --short rebased-feature-branch~1)] second commit
-        Author: A U Thor <author@example.com>
-        Date: Thu Apr 7 15:14:13 2005 -0700
-        2 files changed, 2 insertions(+)
-        create mode 100644 file1
-        create mode 100644 file2
-       Committed: 0001 second commit
-       Merging unrelated-onto-branch with HEAD~0
-       Merging:
-       $(git rev-parse --short rebased-feature-branch~1) second commit
-       $(git rev-parse --short feature-branch) third commit
-       found 1 common ancestor:
-       $(git rev-parse --short feature-branch~1) second commit
-       [detached HEAD $(git rev-parse --short rebased-feature-branch)] third commit
-        Author: A U Thor <author@example.com>
-        Date: Thu Apr 7 15:15:13 2005 -0700
-        1 file changed, 1 insertion(+)
-        create mode 100644 file3
-       Committed: 0002 third commit
-       All done.
-       Applied autostash.
-       EOF
-}
-
 create_expected_failure_am () {
        cat >expected <<-EOF
        $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
@@ -112,43 +77,6 @@ create_expected_failure_interactive () {
        EOF
 }
 
-create_expected_failure_merge () {
-       cat >expected <<-EOF
-       $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
-       HEAD is now at $(git rev-parse --short feature-branch) third commit
-       First, rewinding head to replay your work on top of it...
-       Merging unrelated-onto-branch with HEAD~1
-       Merging:
-       $(git rev-parse --short unrelated-onto-branch) unrelated commit
-       $(git rev-parse --short feature-branch^) second commit
-       found 1 common ancestor:
-       $(git rev-parse --short feature-branch~2) initial commit
-       [detached HEAD $(git rev-parse --short rebased-feature-branch~1)] second commit
-        Author: A U Thor <author@example.com>
-        Date: Thu Apr 7 15:14:13 2005 -0700
-        2 files changed, 2 insertions(+)
-        create mode 100644 file1
-        create mode 100644 file2
-       Committed: 0001 second commit
-       Merging unrelated-onto-branch with HEAD~0
-       Merging:
-       $(git rev-parse --short rebased-feature-branch~1) second commit
-       $(git rev-parse --short feature-branch) third commit
-       found 1 common ancestor:
-       $(git rev-parse --short feature-branch~1) second commit
-       [detached HEAD $(git rev-parse --short rebased-feature-branch)] third commit
-        Author: A U Thor <author@example.com>
-        Date: Thu Apr 7 15:15:13 2005 -0700
-        1 file changed, 1 insertion(+)
-        create mode 100644 file3
-       Committed: 0002 third commit
-       All done.
-       Applying autostash resulted in conflicts.
-       Your changes are safe in the stash.
-       You can run "git stash pop" or "git stash drop" at any time.
-       EOF
-}
-
 testrebase () {
        type=$1
        dotest=$2
@@ -177,6 +105,9 @@ testrebase () {
        test_expect_success "rebase$type --autostash: check output" '
                test_when_finished git branch -D rebased-feature-branch &&
                suffix=${type#\ --} && suffix=${suffix:-am} &&
+               if test ${suffix} = "merge"; then
+                       suffix=interactive
+               fi &&
                create_expected_success_$suffix &&
                test_i18ncmp expected actual
        '
@@ -274,6 +205,9 @@ testrebase () {
        test_expect_success "rebase$type: check output with conflicting stash" '
                test_when_finished git branch -D rebased-feature-branch &&
                suffix=${type#\ --} && suffix=${suffix:-am} &&
+               if test ${suffix} = "merge"; then
+                       suffix=interactive
+               fi &&
                create_expected_failure_$suffix &&
                test_i18ncmp expected actual
        '
index 23ad4cff35a74c9ffb8656f61e777d633459cb77..7274dca40b1ccf0d32aeb98e7b0acec59daaa14d 100755 (executable)
@@ -111,7 +111,7 @@ test_run_rebase () {
        "
 }
 test_run_rebase success ''
-test_run_rebase failure -m
+test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
 
@@ -126,7 +126,7 @@ test_run_rebase () {
        "
 }
 test_run_rebase success ''
-test_run_rebase failure -m
+test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
 
@@ -141,7 +141,7 @@ test_run_rebase () {
        "
 }
 test_run_rebase success ''
-test_run_rebase failure -m
+test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
 
@@ -284,7 +284,7 @@ test_run_rebase () {
        "
 }
 test_run_rebase success ''
-test_run_rebase failure -m
+test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
 
@@ -315,7 +315,7 @@ test_run_rebase () {
        "
 }
 test_run_rebase success ''
-test_run_rebase failure -m
+test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase failure -p
 
index 5f892e33d7e53e8871df256b3344ebd0476712ee..fd8efe84fe8f74d062d73f27fb63d95cc5bed4c8 100755 (executable)
@@ -70,9 +70,8 @@ test_run_rebase () {
                test_linear_range "\'"$expected"\'" d..
        "
 }
-#TODO: make order consistent across all flavors of rebase
-test_run_rebase success 'e n o' ''
-test_run_rebase success 'e n o' -m
+test_run_rebase success 'n o e' ''
+test_run_rebase success 'n o e' -m
 test_run_rebase success 'n o e' -i
 
 test_run_rebase () {
@@ -87,9 +86,8 @@ test_run_rebase () {
                test_linear_range "\'"$expected"\'" c..
        "
 }
-#TODO: make order consistent across all flavors of rebase
-test_run_rebase success 'd e n o' ''
-test_run_rebase success 'd e n o' -m
+test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' -m
 test_run_rebase success 'd n o e' -i
 
 test_run_rebase () {
@@ -104,9 +102,8 @@ test_run_rebase () {
                test_linear_range "\'"$expected"\'" c..
        "
 }
-#TODO: make order consistent across all flavors of rebase
-test_run_rebase success 'd e n o' ''
-test_run_rebase success 'd e n o' -m
+test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' -m
 test_run_rebase success 'd n o e' -i
 
 if ! test_have_prereq REBASE_P; then
index cc5646836fce1aaaa01556450a9b017c7a853088..4c69255ee6b14bdd52b6f840e2e528c182cb23ec 100755 (executable)
@@ -125,7 +125,7 @@ test_expect_success '`reset` refuses to overwrite untracked files' '
        : >dont-overwrite-untracked.t &&
        echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
-       test_must_fail git rebase -r HEAD &&
+       test_must_fail git rebase -ir HEAD &&
        git rebase --abort
 '
 
index b1602718f85468d17faa1cc0d7ae8854ac2f5407..8b635a196d5cc57016520466b92b5f439b217a9d 100755 (executable)
@@ -40,12 +40,12 @@ test_expect_success 'cherry-pick -m complains of bogus numbers' '
        test_expect_code 129 git cherry-pick -m 0 b
 '
 
-test_expect_success 'cherry-pick a non-merge with -m should fail' '
+test_expect_success 'cherry-pick explicit first parent of a non-merge' '
 
        git reset --hard &&
        git checkout a^0 &&
-       test_expect_code 128 git cherry-pick -m 1 b &&
-       git diff --exit-code a --
+       git cherry-pick -m 1 b &&
+       git diff --exit-code c --
 
 '
 
@@ -84,12 +84,12 @@ test_expect_success 'cherry pick a merge relative to nonexistent parent should f
 
 '
 
-test_expect_success 'revert a non-merge with -m should fail' '
+test_expect_success 'revert explicit first parent of a non-merge' '
 
        git reset --hard &&
        git checkout c^0 &&
-       test_must_fail git revert -m 1 b &&
-       git diff --exit-code c
+       git revert -m 1 b &&
+       git diff --exit-code a --
 
 '
 
index fb889ac6f05a3243b09fbb7ec05356ed61ff9cb0..127dd0082ff8f535eeb8dee93095cf1ce13f3a95 100755 (executable)
@@ -64,10 +64,10 @@ test_expect_success 'merge setup' '
        git checkout -b new A
 '
 
-test_expect_success 'cherry-pick a non-merge with --ff and -m should fail' '
+test_expect_success 'cherry-pick explicit first parent of a non-merge with --ff' '
        git reset --hard A -- &&
-       test_must_fail git cherry-pick --ff -m 1 B &&
-       git diff --exit-code A --
+       git cherry-pick --ff -m 1 B &&
+       git diff --exit-code C --
 '
 
 test_expect_success 'cherry pick a merge with --ff but without -m should fail' '
index c84eeefdc9ae0fc0bd5f91cf3eb1a51a6c7d34a2..941d5026da2adc857fa332f899ea3594876a550c 100755 (executable)
@@ -61,7 +61,11 @@ test_expect_success 'cherry-pick mid-cherry-pick-sequence' '
 
 test_expect_success 'cherry-pick persists opts correctly' '
        pristine_detach initial &&
-       test_expect_code 128 git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours initial..anotherpick &&
+       # to make sure that the session to cherry-pick a sequence
+       # gets interrupted, use a high-enough number that is larger
+       # than the number of parents of any commit we have created
+       mainline=4 &&
+       test_expect_code 128 git cherry-pick -s -m $mainline --strategy=recursive -X patience -X ours initial..anotherpick &&
        test_path_is_dir .git/sequencer &&
        test_path_is_file .git/sequencer/head &&
        test_path_is_file .git/sequencer/todo &&
@@ -69,7 +73,7 @@ test_expect_success 'cherry-pick persists opts correctly' '
        echo "true" >expect &&
        git config --file=.git/sequencer/opts --get-all options.signoff >actual &&
        test_cmp expect actual &&
-       echo "1" >expect &&
+       echo "$mainline" >expect &&
        git config --file=.git/sequencer/opts --get-all options.mainline >actual &&
        test_cmp expect actual &&
        echo "recursive" >expect &&
index 37729ba2582119047d559d22df8c522f9c0ab0f1..be582a513b1b99a71c10279aacc72cb36bd4bf7b 100755 (executable)
@@ -402,4 +402,11 @@ test_expect_success 'all statuses changed in folder if . is given' '
        test $(git ls-files --stage | grep ^100755 | wc -l) -eq 0
 '
 
+test_expect_success CASE_INSENSITIVE_FS 'path is case-insensitive' '
+       path="$(pwd)/BLUB" &&
+       touch "$path" &&
+       downcased="$(echo "$path" | tr A-Z a-z)" &&
+       git add "$downcased"
+'
+
 test_done
index cd216655b97b852eec963916e796d11d32eb976a..5f8272b6f94b513b250575c6e99e4f76d6b08e75 100755 (executable)
@@ -1096,4 +1096,32 @@ test_expect_success 'stash -- <subdir> works with binary files' '
        test_path_is_file subdir/untracked
 '
 
+test_expect_success 'stash works when user.name and user.email are not set' '
+       git reset &&
+       >1 &&
+       git add 1 &&
+       echo "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" >expect &&
+       git stash &&
+       git show -s --format="%an <%ae>" refs/stash >actual &&
+       test_cmp expect actual &&
+       >2 &&
+       git add 2 &&
+       test_config user.useconfigonly true &&
+       test_config stash.usebuiltin true &&
+       (
+               sane_unset GIT_AUTHOR_NAME &&
+               sane_unset GIT_AUTHOR_EMAIL &&
+               sane_unset GIT_COMMITTER_NAME &&
+               sane_unset GIT_COMMITTER_EMAIL &&
+               test_unconfig user.email &&
+               test_unconfig user.name &&
+               test_must_fail git commit -m "should fail" &&
+               echo "git stash <git@stash>" >expect &&
+               >2 &&
+               git stash &&
+               git show -s --format="%an <%ae>" refs/stash >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index a8e01eccd158131118019ca7c9b05d08a65d07e3..03489aff14ea72921f66605eeac3ccddd694e923 100755 (executable)
@@ -32,28 +32,37 @@ test_expect_success 'prepare binary file' '
        git commit -m binbin
 '
 
-# test_expect_success '--stat output after text chmod' '
-#      test_chmod -x rezrov &&
-#      echo " 0 files changed" >expect &&
-#      git diff HEAD --stat >actual &&
-#      test_i18ncmp expect actual
-# '
-#
-# test_expect_success '--shortstat output after text chmod' '
-#      git diff HEAD --shortstat >actual &&
-#      test_i18ncmp expect actual
-# '
-#
-# test_expect_success '--stat output after binary chmod' '
-#      test_chmod +x binbin &&
-#      echo " 0 files changed" >expect &&
-#      git diff HEAD --stat >actual &&
-#      test_i18ncmp expect actual
-# '
-#
-# test_expect_success '--shortstat output after binary chmod' '
-#      git diff HEAD --shortstat >actual &&
-#      test_i18ncmp expect actual
-# '
+test_expect_success '--stat output after text chmod' '
+       test_chmod -x rezrov &&
+       cat >expect <<-\EOF &&
+        rezrov | 0
+        1 file changed, 0 insertions(+), 0 deletions(-)
+       EOF
+       git diff HEAD --stat >actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success '--shortstat output after text chmod' '
+       tail -n 1 <expect >expect.short &&
+       git diff HEAD --shortstat >actual &&
+       test_i18ncmp expect.short actual
+'
+
+test_expect_success '--stat output after binary chmod' '
+       test_chmod +x binbin &&
+       cat >expect <<-EOF &&
+        binbin | Bin
+        rezrov |   0
+        2 files changed, 0 insertions(+), 0 deletions(-)
+       EOF
+       git diff HEAD --stat >actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success '--shortstat output after binary chmod' '
+       tail -n 1 <expect >expect.short &&
+       git diff HEAD --shortstat >actual &&
+       test_i18ncmp expect.short actual
+'
 
 test_done
index 7d985ff6b1bff3c73e4309cde675f67c813d93da..9f8f0e84ad4f35b72da73ef8ddfee0a3cddfbe1a 100755 (executable)
@@ -98,6 +98,12 @@ test_expect_success setup '
        git commit -m "update mode" &&
        git checkout -f master &&
 
+       # Same merge as master, but with parents reversed. Hide it in a
+       # pseudo-ref to avoid impacting tests with --all.
+       commit=$(echo reverse |
+                git commit-tree -p master^2 -p master^1 master^{tree}) &&
+       git update-ref REVERSE $commit &&
+
        git config diff.renames false &&
 
        git show-branch
@@ -239,6 +245,8 @@ diff-tree --cc --stat --summary master
 # stat summary should show the diffstat and summary with the first parent
 diff-tree -c --stat --summary side
 diff-tree --cc --stat --summary side
+diff-tree --cc --shortstat master
+diff-tree --cc --summary REVERSE
 # improved by Timo's patch
 diff-tree --cc --patch-with-stat master
 # improved by Timo's patch
@@ -350,6 +358,7 @@ diff --line-prefix=abc master master^ side
 diff --dirstat master~1 master~2
 diff --dirstat initial rearrange
 diff --dirstat-by-file initial rearrange
+diff --dirstat --cc master~1 master
 # No-index --abbrev and --no-abbrev
 diff --raw initial
 :noellipses diff --raw initial
diff --git a/t/t4013/diff.diff-tree_--cc_--shortstat_master b/t/t4013/diff.diff-tree_--cc_--shortstat_master
new file mode 100644 (file)
index 0000000..a4ca42d
--- /dev/null
@@ -0,0 +1,4 @@
+$ git diff-tree --cc --shortstat master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ 2 files changed, 5 insertions(+)
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--summary_REVERSE b/t/t4013/diff.diff-tree_--cc_--summary_REVERSE
new file mode 100644 (file)
index 0000000..e208dd5
--- /dev/null
@@ -0,0 +1,6 @@
+$ git diff-tree --cc --summary REVERSE
+2562325a7ee916efb2481da93073b82cec801cbc
+ create mode 100644 file1
+ delete mode 100644 file2
+ delete mode 100644 file3
+$
diff --git a/t/t4013/diff.diff_--dirstat_--cc_master~1_master b/t/t4013/diff.diff_--dirstat_--cc_master~1_master
new file mode 100644 (file)
index 0000000..fba4e34
--- /dev/null
@@ -0,0 +1,3 @@
+$ git diff --dirstat --cc master~1 master
+  40.0% dir/
+$
index a9fb226c5ad566095163c5141a4ca591f2aeacb1..ab4670d23653eca46292b13ef1559584aae5c3a6 100755 (executable)
@@ -1802,8 +1802,8 @@ test_expect_success 'only move detection ignores white spaces' '
        <BOLD;MAGENTA>-a long line to exceed per-line minimum<RESET>
        <BOLD;MAGENTA>-another long line to exceed per-line minimum<RESET>
        <RED>-original file<RESET>
-       <BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>a long line to exceed per-line minimum<RESET>
-       <BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>another long line to exceed per-line minimum<RESET>
+       <BOLD;CYAN>+<RESET>Q<BOLD;CYAN>a long line to exceed per-line minimum<RESET>
+       <BOLD;CYAN>+<RESET>Q<BOLD;CYAN>another long line to exceed per-line minimum<RESET>
        <GREEN>+<RESET><GREEN>new file<RESET>
        EOF
        test_cmp expected actual
@@ -1827,6 +1827,7 @@ test_expect_success 'compare whitespace delta across moved blocks' '
        QQQthat has similar lines
        QQQto previous blocks, but with different indent
        QQQYetQAnotherQoutlierQ
+       QLine with internal w h i t e s p a c e change
        EOF
 
        git add text.txt &&
@@ -1847,6 +1848,7 @@ test_expect_success 'compare whitespace delta across moved blocks' '
        QQthat has similar lines
        QQto previous blocks, but with different indent
        QQYetQAnotherQoutlier
+       QLine with internal whitespace change
        EOF
 
        git diff --color --color-moved --color-moved-ws=allow-indentation-change >actual.raw &&
@@ -1856,7 +1858,7 @@ test_expect_success 'compare whitespace delta across moved blocks' '
                <BOLD>diff --git a/text.txt b/text.txt<RESET>
                <BOLD>--- a/text.txt<RESET>
                <BOLD>+++ b/text.txt<RESET>
-               <CYAN>@@ -1,14 +1,14 @@<RESET>
+               <CYAN>@@ -1,15 +1,15 @@<RESET>
                <BOLD;MAGENTA>-QIndented<RESET>
                <BOLD;MAGENTA>-QText across<RESET>
                <BOLD;MAGENTA>-Qsome lines<RESET>
@@ -1871,6 +1873,7 @@ test_expect_success 'compare whitespace delta across moved blocks' '
                <BOLD;MAGENTA>-QQQthat has similar lines<RESET>
                <BOLD;MAGENTA>-QQQto previous blocks, but with different indent<RESET>
                <RED>-QQQYetQAnotherQoutlierQ<RESET>
+               <RED>-QLine with internal w h i t e s p a c e change<RESET>
                <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Indented<RESET>
                <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Text across<RESET>
                <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>some lines<RESET>
@@ -1885,11 +1888,30 @@ test_expect_success 'compare whitespace delta across moved blocks' '
                <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>that has similar lines<RESET>
                <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>to previous blocks, but with different indent<RESET>
                <GREEN>+<RESET>QQ<GREEN>YetQAnotherQoutlier<RESET>
+               <GREEN>+<RESET>Q<GREEN>Line with internal whitespace change<RESET>
        EOF
 
        test_cmp expected actual
 '
 
+test_expect_success 'bogus settings in move detection erroring out' '
+       test_must_fail git diff --color-moved=bogus 2>err &&
+       test_i18ngrep "must be one of" err &&
+       test_i18ngrep bogus err &&
+
+       test_must_fail git -c diff.colormoved=bogus diff 2>err &&
+       test_i18ngrep "must be one of" err &&
+       test_i18ngrep "from command-line config" err &&
+
+       test_must_fail git diff --color-moved-ws=bogus 2>err &&
+       test_i18ngrep "possible values" err &&
+       test_i18ngrep bogus err &&
+
+       test_must_fail git -c diff.colormovedws=bogus diff 2>err &&
+       test_i18ngrep "possible values" err &&
+       test_i18ngrep "from command-line config" err
+'
+
 test_expect_success 'compare whitespace delta incompatible with other space options' '
        test_must_fail git diff \
                --color-moved-ws=allow-indentation-change,ignore-all-space \
@@ -1897,4 +1919,93 @@ test_expect_success 'compare whitespace delta incompatible with other space opti
        test_i18ngrep allow-indentation-change err
 '
 
+EMPTY=''
+test_expect_success 'compare mixed whitespace delta across moved blocks' '
+
+       git reset --hard &&
+       tr Q_ "\t " <<-EOF >text.txt &&
+       ${EMPTY}
+       ____too short without
+       ${EMPTY}
+       ___being grouped across blank line
+       ${EMPTY}
+       context
+       lines
+       to
+       anchor
+       ____Indented text to
+       _Q____be further indented by four spaces across
+       ____Qseveral lines
+       QQ____These two lines have had their
+       ____indentation reduced by four spaces
+       Qdifferent indentation change
+       ____too short
+       EOF
+
+       git add text.txt &&
+       git commit -m "add text.txt" &&
+
+       tr Q_ "\t " <<-EOF >text.txt &&
+       context
+       lines
+       to
+       anchor
+       QIndented text to
+       QQbe further indented by four spaces across
+       Q____several lines
+       ${EMPTY}
+       QQtoo short without
+       ${EMPTY}
+       Q_______being grouped across blank line
+       ${EMPTY}
+       Q_QThese two lines have had their
+       indentation reduced by four spaces
+       QQdifferent indentation change
+       __Qtoo short
+       EOF
+
+       git -c color.diff.whitespace="normal red" \
+               -c core.whitespace=space-before-tab \
+               diff --color --color-moved --ws-error-highlight=all \
+               --color-moved-ws=allow-indentation-change >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
+
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/text.txt b/text.txt<RESET>
+       <BOLD>--- a/text.txt<RESET>
+       <BOLD>+++ b/text.txt<RESET>
+       <CYAN>@@ -1,16 +1,16 @@<RESET>
+       <BOLD;MAGENTA>-<RESET>
+       <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>    too short without<RESET>
+       <BOLD;MAGENTA>-<RESET>
+       <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>   being grouped across blank line<RESET>
+       <BOLD;MAGENTA>-<RESET>
+        <RESET>context<RESET>
+        <RESET>lines<RESET>
+        <RESET>to<RESET>
+        <RESET>anchor<RESET>
+       <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>    Indented text to<RESET>
+       <BOLD;MAGENTA>-<RESET><BRED> <RESET>    <BOLD;MAGENTA>    be further indented by four spaces across<RESET>
+       <BOLD;MAGENTA>-<RESET><BRED>    <RESET> <BOLD;MAGENTA>several lines<RESET>
+       <BOLD;BLUE>-<RESET>             <BOLD;BLUE>    These two lines have had their<RESET>
+       <BOLD;BLUE>-<RESET><BOLD;BLUE>    indentation reduced by four spaces<RESET>
+       <BOLD;MAGENTA>-<RESET>  <BOLD;MAGENTA>different indentation change<RESET>
+       <RED>-<RESET><RED>    too short<RESET>
+       <BOLD;CYAN>+<RESET>     <BOLD;CYAN>Indented text to<RESET>
+       <BOLD;CYAN>+<RESET>             <BOLD;CYAN>be further indented by four spaces across<RESET>
+       <BOLD;CYAN>+<RESET>     <BOLD;CYAN>    several lines<RESET>
+       <BOLD;YELLOW>+<RESET>
+       <BOLD;YELLOW>+<RESET>           <BOLD;YELLOW>too short without<RESET>
+       <BOLD;YELLOW>+<RESET>
+       <BOLD;YELLOW>+<RESET>   <BOLD;YELLOW>       being grouped across blank line<RESET>
+       <BOLD;YELLOW>+<RESET>
+       <BOLD;CYAN>+<RESET>     <BRED> <RESET>  <BOLD;CYAN>These two lines have had their<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>indentation reduced by four spaces<RESET>
+       <BOLD;YELLOW>+<RESET>           <BOLD;YELLOW>different indentation change<RESET>
+       <GREEN>+<RESET><BRED>  <RESET>  <GREEN>too short<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
 test_done
diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
new file mode 100755 (executable)
index 0000000..5df6b5e
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='test combined/stat/moved interaction'
+. ./test-lib.sh
+
+# This test covers a weird 3-way interaction between "--cc -p", which will run
+# the combined diff code, along with "--stat", which will be computed as a
+# first-parent stat during the combined diff, and "--color-moved", which
+# enables the emitted_symbols list to store the diff in memory.
+
+test_expect_success 'set up history with a merge' '
+       test_commit A &&
+       test_commit B &&
+       git checkout -b side HEAD^ &&
+       test_commit C &&
+       git merge -m M master &&
+       test_commit D
+'
+
+test_expect_success 'log --cc -p --stat --color-moved' '
+       cat >expect <<-\EOF &&
+       commit D
+       ---
+        D.t | 1 +
+        1 file changed, 1 insertion(+)
+
+       diff --git a/D.t b/D.t
+       new file mode 100644
+       index 0000000..1784810
+       --- /dev/null
+       +++ b/D.t
+       @@ -0,0 +1 @@
+       +D
+       commit M
+
+        B.t | 1 +
+        1 file changed, 1 insertion(+)
+       commit C
+       ---
+        C.t | 1 +
+        1 file changed, 1 insertion(+)
+
+       diff --git a/C.t b/C.t
+       new file mode 100644
+       index 0000000..3cc58df
+       --- /dev/null
+       +++ b/C.t
+       @@ -0,0 +1 @@
+       +C
+       commit B
+       ---
+        B.t | 1 +
+        1 file changed, 1 insertion(+)
+
+       diff --git a/B.t b/B.t
+       new file mode 100644
+       index 0000000..223b783
+       --- /dev/null
+       +++ b/B.t
+       @@ -0,0 +1 @@
+       +B
+       commit A
+       ---
+        A.t | 1 +
+        1 file changed, 1 insertion(+)
+
+       diff --git a/A.t b/A.t
+       new file mode 100644
+       index 0000000..f70f10e
+       --- /dev/null
+       +++ b/A.t
+       @@ -0,0 +1 @@
+       +A
+       EOF
+       git log --format="commit %s" --cc -p --stat --color-moved >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 978a8a66ff05055ad1967b5cc25f36880230a5e4..7df8c3d4ec04ef86e12277e5debfeb8f8f825521 100755 (executable)
@@ -621,4 +621,54 @@ test_expect_success 'trailer parsing not fooled by --- line' '
        test_cmp expect actual
 '
 
+test_expect_success 'set up %S tests' '
+       git checkout --orphan source-a &&
+       test_commit one &&
+       test_commit two &&
+       git checkout -b source-b HEAD^ &&
+       test_commit three
+'
+
+test_expect_success 'log --format=%S paints branch names' '
+       cat >expect <<-\EOF &&
+       source-b
+       source-a
+       source-b
+       EOF
+       git log --format=%S source-a source-b >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log --format=%S paints tag names' '
+       git tag -m tagged source-tag &&
+       cat >expect <<-\EOF &&
+       source-tag
+       source-a
+       source-tag
+       EOF
+       git log --format=%S source-tag source-a >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log --format=%S paints symmetric ranges' '
+       cat >expect <<-\EOF &&
+       source-b
+       source-a
+       EOF
+       git log --format=%S source-a...source-b >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '%S in git log --format works with other placeholders (part 1)' '
+       git log --format="source-b %h" source-b >expect &&
+       git log --format="%S %h" source-b >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '%S in git log --format works with other placeholders (part 2)' '
+       git log --format="%h source-b" source-b >expect &&
+       git log --format="%h %S" source-b >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 844df760f7d1c2b1b25eb01fada039efd0354711..5d06f5f45eace6010c1a003e03a814b5ba03f324 100755 (executable)
@@ -106,4 +106,39 @@ test_expect_success 'log -S --no-textconv (missing textconv tool)' '
        rm .gitattributes
 '
 
+test_expect_success 'setup log -[GS] binary & --text' '
+       git checkout --orphan GS-binary-and-text &&
+       git read-tree --empty &&
+       printf "a\na\0a\n" >data.bin &&
+       git add data.bin &&
+       git commit -m "create binary file" data.bin &&
+       printf "a\na\0a\n" >>data.bin &&
+       git commit -m "modify binary file" data.bin &&
+       git rm data.bin &&
+       git commit -m "delete binary file" data.bin &&
+       git log >full-log
+'
+
+test_expect_success 'log -G ignores binary files' '
+       git log -Ga >log &&
+       test_must_be_empty log
+'
+
+test_expect_success 'log -G looks into binary files with -a' '
+       git log -a -Ga >log &&
+       test_cmp log full-log
+'
+
+test_expect_success 'log -G looks into binary files with textconv filter' '
+       test_when_finished "rm .gitattributes" &&
+       echo "* diff=bin" >.gitattributes &&
+       git -c diff.bin.textconv=cat log -Ga >log &&
+       test_cmp log full-log
+'
+
+test_expect_success 'log -S looks into binary files' '
+       git log -Sa >log &&
+       test_cmp log full-log
+'
+
 test_done
index ef1322148e1413e4b90f46b6bea4f2d9b0300189..bd5fe4d1483005175d9c2211dee1901c2b132573 100755 (executable)
@@ -25,7 +25,7 @@ canned_test_failure () {
 test_bad_opts () {
        test_expect_success "invalid args: $1" "
                test_must_fail git log $1 2>errors &&
-               grep '$2' errors
+               test_i18ngrep '$2' errors
        "
 }
 
index ced44355cab99fc4f5fd9768daf10f5a2c1b21c7..271eb5a1fdfbfe4aab216271fc1897968283639d 100755 (executable)
@@ -3,8 +3,12 @@
 test_description='test corner cases of git-archive'
 . ./test-lib.sh
 
-test_expect_success 'create commit with empty tree' '
-       git commit --allow-empty -m foo
+# the 10knuls.tar file is used to test for an empty git generated tar
+# without having to invoke tar because an otherwise valid empty GNU tar
+# will be considered broken by {Open,Net}BSD tar
+test_expect_success 'create commit with empty tree and fake empty tar' '
+       git commit --allow-empty -m foo &&
+       perl -e "print \"\\0\" x 10240" >10knuls.tar
 '
 
 # Make a dir and clean it up afterwards
@@ -47,7 +51,6 @@ test_expect_success HEADER_ONLY_TAR_OK 'tar archive of commit with empty tree' '
 
 test_expect_success 'tar archive of empty tree is empty' '
        git archive --format=tar HEAD: >empty.tar &&
-       perl -e "print \"\\0\" x 10240" >10knuls.tar &&
        test_cmp_bin 10knuls.tar empty.tar
 '
 
@@ -106,16 +109,12 @@ test_expect_success 'create a commit with an empty subtree' '
 
 test_expect_success 'archive empty subtree with no pathspec' '
        git archive --format=tar $root_tree >subtree-all.tar &&
-       make_dir extract &&
-       "$TAR" xf subtree-all.tar -C extract &&
-       check_dir extract
+       test_cmp_bin 10knuls.tar subtree-all.tar
 '
 
 test_expect_success 'archive empty subtree by direct pathspec' '
        git archive --format=tar $root_tree -- sub >subtree-path.tar &&
-       make_dir extract &&
-       "$TAR" xf subtree-path.tar -C extract &&
-       check_dir extract
+       test_cmp_bin 10knuls.tar subtree-path.tar
 '
 
 ZIPINFO=zipinfo
index 5fe21db99f43aa79dc36ab36bf6d40ca2f47264d..16d10ebce82fa1fc2e4396f8d30911541b96d7b6 100755 (executable)
@@ -122,7 +122,7 @@ test_expect_success 'write graph with merges' '
        cd "$TRASH_DIRECTORY/full" &&
        git commit-graph write &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "10" "large_edges"
+       graph_read_expect "10" "extra_edges"
 '
 
 graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
@@ -157,7 +157,7 @@ test_expect_success 'write graph with new commit' '
        cd "$TRASH_DIRECTORY/full" &&
        git commit-graph write &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "11" "large_edges"
+       graph_read_expect "11" "extra_edges"
 '
 
 graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -167,7 +167,7 @@ test_expect_success 'write graph with nothing new' '
        cd "$TRASH_DIRECTORY/full" &&
        git commit-graph write &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "11" "large_edges"
+       graph_read_expect "11" "extra_edges"
 '
 
 graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -177,7 +177,7 @@ test_expect_success 'build graph from latest pack with closure' '
        cd "$TRASH_DIRECTORY/full" &&
        cat new-idx | git commit-graph write --stdin-packs &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "9" "large_edges"
+       graph_read_expect "9" "extra_edges"
 '
 
 graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
@@ -200,7 +200,7 @@ test_expect_success 'build graph from commits with append' '
        cd "$TRASH_DIRECTORY/full" &&
        git rev-parse merge/3 | git commit-graph write --stdin-commits --append &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "10" "large_edges"
+       graph_read_expect "10" "extra_edges"
 '
 
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -210,7 +210,7 @@ test_expect_success 'build graph using --reachable' '
        cd "$TRASH_DIRECTORY/full" &&
        git commit-graph write --reachable &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "11" "large_edges"
+       graph_read_expect "11" "extra_edges"
 '
 
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -231,7 +231,7 @@ test_expect_success 'write graph in bare repo' '
        cd "$TRASH_DIRECTORY/bare" &&
        git commit-graph write &&
        test_path_is_file $baredir/info/commit-graph &&
-       graph_read_expect "11" "large_edges"
+       graph_read_expect "11" "extra_edges"
 '
 
 graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
@@ -366,9 +366,10 @@ GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \
 GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
 GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
 
-# usage: corrupt_graph_and_verify <position> <data> <string>
+# usage: corrupt_graph_and_verify <position> <data> <string> [<zero_pos>]
 # Manipulates the commit-graph file at the position
-# by inserting the data, then runs 'git commit-graph verify'
+# by inserting the data, optionally zeroing the file
+# starting at <zero_pos>, then runs 'git commit-graph verify'
 # and places the output in the file 'err'. Test 'err' for
 # the given string.
 corrupt_graph_and_verify() {
@@ -376,11 +377,15 @@ corrupt_graph_and_verify() {
        data="${2:-\0}"
        grepstr=$3
        cd "$TRASH_DIRECTORY/full" &&
+       orig_size=$(wc -c < $objdir/info/commit-graph) &&
+       zero_pos=${4:-${orig_size}} &&
        test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
        cp $objdir/info/commit-graph commit-graph-backup &&
        printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
+       dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" count=0 &&
+       dd if=/dev/zero of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" count=$(($orig_size - $zero_pos)) &&
        test_must_fail git commit-graph verify 2>test_err &&
-       grep -v "^+" test_err >err
+       grep -v "^+" test_err >err &&
        test_i18ngrep "$grepstr" err
 }
 
@@ -484,6 +489,11 @@ test_expect_success 'detect invalid checksum hash' '
                "incorrect checksum"
 '
 
+test_expect_success 'detect incorrect chunk count' '
+       corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\377" \
+               "chunk lookup table entry missing" $GRAPH_CHUNK_LOOKUP_OFFSET
+'
+
 test_expect_success 'git fsck (checks commit-graph)' '
        cd "$TRASH_DIRECTORY/full" &&
        git fsck &&
index fc898c9eac704c934cfbef47fc1463f27dd50c6f..a539ffc0803a746c70d458a95f27144c9039afff 100755 (executable)
@@ -7,82 +7,70 @@ test_description='Test the post-checkout hook.'
 . ./test-lib.sh
 
 test_expect_success setup '
-       echo Data for commit0. >a &&
-       echo Data for commit0. >b &&
-       git update-index --add a &&
-       git update-index --add b &&
-       tree0=$(git write-tree) &&
-       commit0=$(echo setup | git commit-tree $tree0) &&
-       git update-ref refs/heads/master $commit0 &&
-       git clone ./. clone1 &&
-       git clone ./. clone2 &&
-       GIT_DIR=clone2/.git git branch new2 &&
-       echo Data for commit1. >clone2/b &&
-       GIT_DIR=clone2/.git git add clone2/b &&
-       GIT_DIR=clone2/.git git commit -m new2
-'
-
-for clone in 1 2; do
-    cat >clone${clone}/.git/hooks/post-checkout <<'EOF'
-#!/bin/sh
-echo $@ > $GIT_DIR/post-checkout.args
-EOF
-    chmod u+x clone${clone}/.git/hooks/post-checkout
-done
-
-test_expect_success 'post-checkout runs as expected ' '
-       GIT_DIR=clone1/.git git checkout master &&
-       test -e clone1/.git/post-checkout.args
+       mkdir -p .git/hooks &&
+       write_script .git/hooks/post-checkout <<-\EOF &&
+       echo "$@" >.git/post-checkout.args
+       EOF
+       test_commit one &&
+       test_commit two &&
+       test_commit rebase-on-me &&
+       git reset --hard HEAD^ &&
+       test_commit three
 '
 
 test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' '
-       old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
-       new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
-       flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
+       test_when_finished "rm -f .git/post-checkout.args" &&
+       git checkout master &&
+       read old new flag <.git/post-checkout.args &&
        test $old = $new && test $flag = 1
 '
 
-test_expect_success 'post-checkout runs as expected ' '
-       GIT_DIR=clone1/.git git checkout master &&
-       test -e clone1/.git/post-checkout.args
-'
-
 test_expect_success 'post-checkout args are correct with git checkout -b ' '
-       GIT_DIR=clone1/.git git checkout -b new1 &&
-       old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
-       new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
-       flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
+       test_when_finished "rm -f .git/post-checkout.args" &&
+       git checkout -b new1 &&
+       read old new flag <.git/post-checkout.args &&
        test $old = $new && test $flag = 1
 '
 
 test_expect_success 'post-checkout receives the right args with HEAD changed ' '
-       GIT_DIR=clone2/.git git checkout new2 &&
-       old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
-       new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
-       flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
+       test_when_finished "rm -f .git/post-checkout.args" &&
+       git checkout two &&
+       read old new flag <.git/post-checkout.args &&
        test $old != $new && test $flag = 1
 '
 
 test_expect_success 'post-checkout receives the right args when not switching branches ' '
-       GIT_DIR=clone2/.git git checkout master b &&
-       old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
-       new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
-       flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
+       test_when_finished "rm -f .git/post-checkout.args" &&
+       git checkout master -- three.t &&
+       read old new flag <.git/post-checkout.args &&
        test $old = $new && test $flag = 0
 '
 
-if test "$(git config --bool core.filemode)" = true; then
-mkdir -p templates/hooks
-cat >templates/hooks/post-checkout <<'EOF'
-#!/bin/sh
-echo $@ > $GIT_DIR/post-checkout.args
-EOF
-chmod +x templates/hooks/post-checkout
+test_expect_success 'post-checkout is triggered on rebase' '
+       test_when_finished "rm -f .git/post-checkout.args" &&
+       git checkout -b rebase-test master &&
+       rm -f .git/post-checkout.args &&
+       git rebase rebase-on-me &&
+       read old new flag <.git/post-checkout.args &&
+       test $old != $new && test $flag = 1
+'
+
+test_expect_success 'post-checkout is triggered on rebase with fast-forward' '
+       test_when_finished "rm -f .git/post-checkout.args" &&
+       git checkout -b ff-rebase-test rebase-on-me^ &&
+       rm -f .git/post-checkout.args &&
+       git rebase rebase-on-me &&
+       read old new flag <.git/post-checkout.args &&
+       test $old != $new && test $flag = 1
+'
 
 test_expect_success 'post-checkout hook is triggered by clone' '
+       mkdir -p templates/hooks &&
+       write_script templates/hooks/post-checkout <<-\EOF &&
+       echo "$@" >$GIT_DIR/post-checkout.args
+       EOF
        git clone --template=templates . clone3 &&
        test -f clone3/.git/post-checkout.args
 '
-fi
 
 test_done
index 9b2a274c71f40ace060069212055a29f9a3c4203..a4a5903cbaee5c2a67963d8746d0252ba59bf163 100755 (executable)
@@ -78,6 +78,7 @@ test_expect_success 'git rebase --skip' '
        git rebase --continue &&
        echo rebase >expected.args &&
        cat >expected.data <<-EOF &&
+       $(git rev-parse C) $(git rev-parse HEAD^)
        $(git rev-parse D) $(git rev-parse HEAD)
        EOF
        verify_hook_input
@@ -91,6 +92,7 @@ test_expect_success 'git rebase --skip the last one' '
        echo rebase >expected.args &&
        cat >expected.data <<-EOF &&
        $(git rev-parse E) $(git rev-parse HEAD)
+       $(git rev-parse F) $(git rev-parse HEAD)
        EOF
        verify_hook_input
 '
@@ -120,6 +122,38 @@ test_expect_success 'git rebase -m --skip' '
        git rebase --continue &&
        echo rebase >expected.args &&
        cat >expected.data <<-EOF &&
+       $(git rev-parse C) $(git rev-parse HEAD^)
+       $(git rev-parse D) $(git rev-parse HEAD)
+       EOF
+       verify_hook_input
+'
+
+test_expect_success 'git rebase with implicit use of interactive backend' '
+       git reset --hard D &&
+       clear_hook_input &&
+       test_must_fail git rebase --keep --onto A B &&
+       echo C > foo &&
+       git add foo &&
+       git rebase --continue &&
+       echo rebase >expected.args &&
+       cat >expected.data <<-EOF &&
+       $(git rev-parse C) $(git rev-parse HEAD^)
+       $(git rev-parse D) $(git rev-parse HEAD)
+       EOF
+       verify_hook_input
+'
+
+test_expect_success 'git rebase --skip with implicit use of interactive backend' '
+       git reset --hard D &&
+       clear_hook_input &&
+       test_must_fail git rebase --keep --onto A B &&
+       test_must_fail git rebase --skip &&
+       echo D > foo &&
+       git add foo &&
+       git rebase --continue &&
+       echo rebase >expected.args &&
+       cat >expected.data <<-EOF &&
+       $(git rev-parse C) $(git rev-parse HEAD^)
        $(git rev-parse D) $(git rev-parse HEAD)
        EOF
        verify_hook_input
index f81b6813c03090fe932852f86ce4b3517c914d7a..2a8c4496618523ebdbd03eb152a1e8189fafcd0a 100755 (executable)
@@ -17,6 +17,7 @@ test_expect_success 'setup' '
        echo " " "error: leading space"
        echo "    "
        echo Err
+       echo SUCCESS
        exit 0
        EOF
        echo 1 >file &&
@@ -35,6 +36,7 @@ test_expect_success 'keywords' '
        grep "<BOLD;RED>error<RESET>: error" decoded &&
        grep "<YELLOW>hint<RESET>:" decoded &&
        grep "<BOLD;GREEN>success<RESET>:" decoded &&
+       grep "<BOLD;GREEN>SUCCESS<RESET>" decoded &&
        grep "<BOLD;YELLOW>warning<RESET>:" decoded
 '
 
index 086f2c40f68ffaf666a09d88b86709133559fc91..49c540b1e1dbd5fc7d9a7baa36ee93a2ed3fc3c3 100755 (executable)
@@ -439,15 +439,23 @@ test_expect_success 'setup tests for the --stdin parameter' '
        ) >input.dup
 '
 
-test_expect_success 'fetch refs from cmdline' '
-       (
-               cd client &&
-               git fetch-pack --no-progress .. $(cat ../input)
-       ) >output &&
-       cut -d " " -f 2 <output | sort >actual &&
-       test_cmp expect actual
+test_expect_success 'setup fetch refs from cmdline v[12]' '
+       cp -r client client1 &&
+       cp -r client client2
 '
 
+for version in '' 1 2
+do
+       test_expect_success "protocol.version=$version fetch refs from cmdline" "
+               (
+                       cd client$version &&
+                       GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. \$(cat ../input)
+               ) >output &&
+               cut -d ' ' -f 2 <output | sort >actual &&
+               test_cmp expect actual
+       "
+done
+
 test_expect_success 'fetch refs from stdin' '
        (
                cd client &&
index d2a2cdd453396b1dba5525f32b6325ac8a10d95d..883b32efa024d97ac74e9ae5763dc8a218251675 100755 (executable)
@@ -1222,4 +1222,59 @@ test_expect_success 'add remote matching the "insteadOf" URL' '
        git remote add backup xyz@example.com
 '
 
+test_expect_success 'unqualified <dst> refspec DWIM and advice' '
+       test_when_finished "(cd test && git tag -d some-tag)" &&
+       (
+               cd test &&
+               git tag -a -m "Some tag" some-tag master &&
+               exit_with=true &&
+               for type in commit tag tree blob
+               do
+                       if test "$type" = "blob"
+                       then
+                               oid=$(git rev-parse some-tag:file)
+                       else
+                               oid=$(git rev-parse some-tag^{$type})
+                       fi &&
+                       test_must_fail git push origin $oid:dst 2>err &&
+                       test_i18ngrep "error: The destination you" err &&
+                       test_i18ngrep "hint: Did you mean" err &&
+                       test_must_fail git -c advice.pushUnqualifiedRefName=false \
+                               push origin $oid:dst 2>err &&
+                       test_i18ngrep "error: The destination you" err &&
+                       test_i18ngrep ! "hint: Did you mean" err ||
+                       exit_with=false
+               done &&
+               $exit_with
+       )
+'
+
+test_expect_success 'refs/remotes/* <src> refspec and unqualified <dst> DWIM and advice' '
+       (
+               cd two &&
+               git tag -a -m "Some tag" my-tag master &&
+               git update-ref refs/trees/my-head-tree HEAD^{tree} &&
+               git update-ref refs/blobs/my-file-blob HEAD:file
+       ) &&
+       (
+               cd test &&
+               git config --add remote.two.fetch "+refs/tags/*:refs/remotes/tags-from-two/*" &&
+               git config --add remote.two.fetch "+refs/trees/*:refs/remotes/trees-from-two/*" &&
+               git config --add remote.two.fetch "+refs/blobs/*:refs/remotes/blobs-from-two/*" &&
+               git fetch --no-tags two &&
+
+               test_must_fail git push origin refs/remotes/two/another:dst 2>err &&
+               test_i18ngrep "error: The destination you" err &&
+
+               test_must_fail git push origin refs/remotes/tags-from-two/my-tag:dst-tag 2>err &&
+               test_i18ngrep "error: The destination you" err &&
+
+               test_must_fail git push origin refs/remotes/trees-from-two/my-head-tree:dst-tree 2>err &&
+               test_i18ngrep "error: The destination you" err &&
+
+               test_must_fail git push origin refs/remotes/blobs-from-two/my-file-blob:dst-blob 2>err &&
+               test_i18ngrep "error: The destination you" err
+       )
+'
+
 test_done
index 32e722db2ed96f14b6ca4b0fe53636c719f57eb5..ced15ae1224247355125fcbad7ad2184f9861cbe 100755 (executable)
@@ -204,6 +204,12 @@ test_expect_success 'overrides work between mixed transfer/upload-pack hideRefs'
        grep refs/tags/magic actual
 '
 
+test_expect_success 'protocol v2 supports hiderefs' '
+       test_config uploadpack.hiderefs refs/tags &&
+       git -c protocol.version=2 ls-remote . >actual &&
+       ! grep refs/tags actual
+'
+
 test_expect_success 'ls-remote --symref' '
        git fetch origin &&
        cat >expect <<-EOF &&
@@ -260,7 +266,7 @@ test_lazy_prereq GIT_DAEMON '
 # This test spawns a daemon, so run it only if the user would be OK with
 # testing with git-daemon.
 test_expect_success PIPE,JGIT,GIT_DAEMON 'indicate no refs in standards-compliant empty remote' '
-       JGIT_DAEMON_PORT=${JGIT_DAEMON_PORT-${this_test#t}} &&
+       test_set_port JGIT_DAEMON_PORT &&
        JGIT_DAEMON_PID= &&
        git init --bare empty.git &&
        >empty.git/git-daemon-export-ok &&
index 6c2f9b2ba266ad910769745e72285a840b33238b..63205dfdf962dc31c9db5ba038674e12760a9909 100755 (executable)
@@ -524,6 +524,8 @@ test_expect_success 'fetching submodules respects parallel settings' '
        git config fetch.recurseSubmodules true &&
        (
                cd downstream &&
+               GIT_TRACE=$(pwd)/trace.out git fetch &&
+               grep "1 tasks" trace.out &&
                GIT_TRACE=$(pwd)/trace.out git fetch --jobs 7 &&
                grep "7 tasks" trace.out &&
                git config submodule.fetchJobs 8 &&
@@ -600,4 +602,121 @@ test_expect_success "fetch new commits when submodule got renamed" '
        test_cmp expect actual
 '
 
+test_expect_success "fetch new submodule commits on-demand outside standard refspec" '
+       # add a second submodule and ensure it is around in downstream first
+       git clone submodule sub1 &&
+       git submodule add ./sub1 &&
+       git commit -m "adding a second submodule" &&
+       git -C downstream pull &&
+       git -C downstream submodule update --init --recursive &&
+
+       git checkout --detach &&
+
+       C=$(git -C submodule commit-tree -m "new change outside refs/heads" HEAD^{tree}) &&
+       git -C submodule update-ref refs/changes/1 $C &&
+       git update-index --cacheinfo 160000 $C submodule &&
+       test_tick &&
+
+       D=$(git -C sub1 commit-tree -m "new change outside refs/heads" HEAD^{tree}) &&
+       git -C sub1 update-ref refs/changes/2 $D &&
+       git update-index --cacheinfo 160000 $D sub1 &&
+
+       git commit -m "updated submodules outside of refs/heads" &&
+       E=$(git rev-parse HEAD) &&
+       git update-ref refs/changes/3 $E &&
+       (
+               cd downstream &&
+               git fetch --recurse-submodules origin refs/changes/3:refs/heads/my_branch &&
+               git -C submodule cat-file -t $C &&
+               git -C sub1 cat-file -t $D &&
+               git checkout --recurse-submodules FETCH_HEAD
+       )
+'
+
+test_expect_success 'fetch new submodule commit on-demand in FETCH_HEAD' '
+       # depends on the previous test for setup
+
+       C=$(git -C submodule commit-tree -m "another change outside refs/heads" HEAD^{tree}) &&
+       git -C submodule update-ref refs/changes/4 $C &&
+       git update-index --cacheinfo 160000 $C submodule &&
+       test_tick &&
+
+       D=$(git -C sub1 commit-tree -m "another change outside refs/heads" HEAD^{tree}) &&
+       git -C sub1 update-ref refs/changes/5 $D &&
+       git update-index --cacheinfo 160000 $D sub1 &&
+
+       git commit -m "updated submodules outside of refs/heads" &&
+       E=$(git rev-parse HEAD) &&
+       git update-ref refs/changes/6 $E &&
+       (
+               cd downstream &&
+               git fetch --recurse-submodules origin refs/changes/6 &&
+               git -C submodule cat-file -t $C &&
+               git -C sub1 cat-file -t $D &&
+               git checkout --recurse-submodules FETCH_HEAD
+       )
+'
+
+test_expect_success 'fetch new submodule commits on-demand without .gitmodules entry' '
+       # depends on the previous test for setup
+
+       git config -f .gitmodules --remove-section submodule.sub1 &&
+       git add .gitmodules &&
+       git commit -m "delete gitmodules file" &&
+       git checkout -B master &&
+       git -C downstream fetch &&
+       git -C downstream checkout origin/master &&
+
+       C=$(git -C submodule commit-tree -m "yet another change outside refs/heads" HEAD^{tree}) &&
+       git -C submodule update-ref refs/changes/7 $C &&
+       git update-index --cacheinfo 160000 $C submodule &&
+       test_tick &&
+
+       D=$(git -C sub1 commit-tree -m "yet another change outside refs/heads" HEAD^{tree}) &&
+       git -C sub1 update-ref refs/changes/8 $D &&
+       git update-index --cacheinfo 160000 $D sub1 &&
+
+       git commit -m "updated submodules outside of refs/heads" &&
+       E=$(git rev-parse HEAD) &&
+       git update-ref refs/changes/9 $E &&
+       (
+               cd downstream &&
+               git fetch --recurse-submodules origin refs/changes/9 &&
+               git -C submodule cat-file -t $C &&
+               git -C sub1 cat-file -t $D &&
+               git checkout --recurse-submodules FETCH_HEAD
+       )
+'
+
+test_expect_success 'fetch new submodule commit intermittently referenced by superproject' '
+       # depends on the previous test for setup
+
+       D=$(git -C sub1 commit-tree -m "change 10 outside refs/heads" HEAD^{tree}) &&
+       E=$(git -C sub1 commit-tree -m "change 11 outside refs/heads" HEAD^{tree}) &&
+       F=$(git -C sub1 commit-tree -m "change 12 outside refs/heads" HEAD^{tree}) &&
+
+       git -C sub1 update-ref refs/changes/10 $D &&
+       git update-index --cacheinfo 160000 $D sub1 &&
+       git commit -m "updated submodules outside of refs/heads" &&
+
+       git -C sub1 update-ref refs/changes/11 $E &&
+       git update-index --cacheinfo 160000 $E sub1 &&
+       git commit -m "updated submodules outside of refs/heads" &&
+
+       git -C sub1 update-ref refs/changes/12 $F &&
+       git update-index --cacheinfo 160000 $F sub1 &&
+       git commit -m "updated submodules outside of refs/heads" &&
+
+       G=$(git rev-parse HEAD) &&
+       git update-ref refs/changes/13 $G &&
+       (
+               cd downstream &&
+               git fetch --recurse-submodules origin refs/changes/13 &&
+
+               git -C sub1 cat-file -t $D &&
+               git -C sub1 cat-file -t $E &&
+               git -C sub1 cat-file -t $F
+       )
+'
+
 test_done
index 6faf17e17a133f31245f89fd0fe9bad1687ac5fa..6caf628efaaa400473ddfb46fdef0b847b4edd39 100755 (executable)
@@ -243,7 +243,8 @@ test_expect_success 'shallow fetches check connectivity before writing shallow f
               "$(git -C "$REPO" rev-parse HEAD)" \
               "$(git -C "$REPO" rev-parse HEAD^)" \
               >"$HTTPD_ROOT_PATH/one-time-sed" &&
-       test_must_fail git -C client fetch --depth=1 "$HTTPD_URL/one_time_sed/repo" \
+       test_must_fail env GIT_TEST_SIDEBAND_ALL=0 git -C client \
+               fetch --depth=1 "$HTTPD_URL/one_time_sed/repo" \
                master:a_branch &&
 
        # Ensure that the one-time-sed script was used.
index 7466aad111fe4ef11b97d05a9616f8530993c288..58ee7876853161087c256e56e687f0b341fde915 100755 (executable)
@@ -183,19 +183,6 @@ test_expect_success 'hostname cannot break out of directory' '
                git ls-remote "$GIT_DAEMON_URL/escape.git"
 '
 
-test_expect_success 'daemon log records all attributes' '
-       cat >expect <<-\EOF &&
-       Extended attribute "host": localhost
-       Extended attribute "protocol": version=1
-       EOF
-       >daemon.log &&
-       GIT_OVERRIDE_VIRTUAL_HOST=localhost \
-               git -c protocol.version=1 \
-                       ls-remote "$GIT_DAEMON_URL/interp.git" &&
-       grep -i extended.attribute daemon.log | cut -d" " -f2- >actual &&
-       test_cmp expect actual
-'
-
 test_expect_success FAKENC 'hostname interpolation works after LF-stripping' '
        {
                printf "git-upload-pack /interp.git\n\0host=localhost" | packetize
index ba548df4a918243ee1b2bcedab525e6ec7eff7d6..217adf3a632b49e9322de1db78c3e1a588c3b7bd 100755 (executable)
@@ -40,6 +40,11 @@ test_expect_success clone '
        git clone "file://$UNCPATH" clone
 '
 
+test_expect_success 'clone with backslashed path' '
+       BACKSLASHED="$(echo "$UNCPATH" | tr / \\\\)" &&
+       git clone "$BACKSLASHED" backslashed
+'
+
 test_expect_success push '
        (
                cd clone &&
diff --git a/t/t5581-http-curl-verbose.sh b/t/t5581-http-curl-verbose.sh
new file mode 100755 (executable)
index 0000000..cd9283e
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+test_description='test GIT_CURL_VERBOSE'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" --bare init &&
+       git config push.default matching &&
+       echo content >file &&
+       git add file &&
+       git commit -m one &&
+       git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git push public master:master
+'
+
+test_expect_success 'failure in git-upload-pack is shown' '
+       test_might_fail env GIT_CURL_VERBOSE=1 \
+               git clone "$HTTPD_URL/error_git_upload_pack/smart/repo.git" \
+               2>curl_log &&
+       grep "< HTTP/1.1 500 Intentional Breakage" curl_log
+'
+
+stop_httpd
+
+test_done
index 8bbc7068acbd1eab9f0499ff1151abd58a87079c..d6948cbdab03cf827d86511ade26366e89fda149 100755 (executable)
@@ -487,7 +487,7 @@ test_clone_url () {
        expect_ssh "$@"
 }
 
-test_expect_success !MINGW 'clone c:temp is ssl' '
+test_expect_success !MINGW,!CYGWIN 'clone c:temp is ssl' '
        test_clone_url c:temp c temp
 '
 
index 39329eb7a8a64b177794b83ef8828b866fead547..60c1ba951b7d4178708574d10986659749925059 100755 (executable)
@@ -45,6 +45,53 @@ test_expect_success 'clone -c config is available during clone' '
        test_cmp expect child/file
 '
 
+test_expect_success 'clone -c remote.origin.fetch=<refspec> works' '
+       rm -rf child &&
+       git update-ref refs/grab/it refs/heads/master &&
+       git update-ref refs/leave/out refs/heads/master &&
+       git clone -c "remote.origin.fetch=+refs/grab/*:refs/grab/*" . child &&
+       git -C child for-each-ref --format="%(refname)" >actual &&
+
+       cat >expect <<-\EOF &&
+       refs/grab/it
+       refs/heads/master
+       refs/remotes/origin/HEAD
+       refs/remotes/origin/master
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'git -c remote.origin.fetch=<refspec> clone works' '
+       rm -rf child &&
+       git -c "remote.origin.fetch=+refs/grab/*:refs/grab/*" clone . child &&
+       git -C child for-each-ref --format="%(refname)" >actual &&
+
+       cat >expect <<-\EOF &&
+       refs/grab/it
+       refs/heads/master
+       refs/remotes/origin/HEAD
+       refs/remotes/origin/master
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '
+       rm -rf child &&
+       git clone --origin=upstream \
+                 -c "remote.upstream.fetch=+refs/grab/*:refs/grab/*" \
+                 -c "remote.origin.fetch=+refs/leave/*:refs/leave/*" \
+                 . child &&
+       git -C child for-each-ref --format="%(refname)" >actual &&
+
+       cat >expect <<-\EOF &&
+       refs/grab/it
+       refs/heads/master
+       refs/remotes/upstream/HEAD
+       refs/remotes/upstream/master
+       EOF
+       test_cmp expect actual
+'
+
 # Tests for the hidden file attribute on windows
 is_hidden () {
        # Use the output of `attrib`, ignore the absolute path
index 336f02a41a66b48269e8eb6030d5323cffb8b686..9643acb1615fe0d28a7f0e386fa69a570a93dc8e 100755 (executable)
@@ -281,7 +281,7 @@ test_expect_success 'upon cloning, check that all refs point to objects' '
        test_must_fail git -c protocol.version=2 clone \
                --filter=blob:none $HTTPD_URL/one_time_sed/server repo 2>err &&
 
-       grep "did not send all necessary objects" err &&
+       test_i18ngrep "did not send all necessary objects" err &&
 
        # Ensure that the one-time-sed script was used.
        ! test -e "$HTTPD_ROOT_PATH/one-time-sed"
index ae79c6bbc0d66e9a7a184b2d1726a316a0affbbe..fe45bf828d09cd9d95a66742ef938837d35141a3 100755 (executable)
@@ -14,7 +14,7 @@ test_expect_success 'test capability advertisement' '
        0000
        EOF
 
-       git serve --advertise-capabilities >out &&
+       GIT_TEST_SIDEBAND_ALL=0 git serve --advertise-capabilities >out &&
        test-tool pkt-line unpack <out >actual &&
        test_cmp expect actual
 '
index 0f2b09ebb8d6625b527ccc771c4725d2a314d4ee..db4ae09f2f81ff77b9c29b99c21363e66c81f222 100755 (executable)
@@ -471,6 +471,53 @@ test_expect_success 'upload-pack respects client shallows' '
        grep "fetch< version 2" trace
 '
 
+test_expect_success 'ensure that multiple fetches in same process from a shallow repo works' '
+       rm -rf server client trace &&
+
+       test_create_repo server &&
+       test_commit -C server one &&
+       test_commit -C server two &&
+       test_commit -C server three &&
+       git clone --shallow-exclude two "file://$(pwd)/server" client &&
+
+       git -C server tag -a -m "an annotated tag" twotag two &&
+
+       # Triggers tag following (thus, 2 fetches in one process)
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
+               fetch --shallow-exclude one origin &&
+       # Ensure that protocol v2 is used
+       grep "fetch< version 2" trace
+'
+
+test_expect_success 'deepen-relative' '
+       rm -rf server client trace &&
+
+       test_create_repo server &&
+       test_commit -C server one &&
+       test_commit -C server two &&
+       test_commit -C server three &&
+       git clone --depth 1 "file://$(pwd)/server" client &&
+       test_commit -C server four &&
+
+       # Sanity check that only "three" is downloaded
+       git -C client log --pretty=tformat:%s master >actual &&
+       echo three >expected &&
+       test_cmp expected actual &&
+
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
+               fetch --deepen=1 origin &&
+       # Ensure that protocol v2 is used
+       grep "fetch< version 2" trace &&
+
+       git -C client log --pretty=tformat:%s origin/master >actual &&
+       cat >expected <<-\EOF &&
+       four
+       three
+       two
+       EOF
+       test_cmp expected actual
+'
+
 # Test protocol v2 with 'http://' transport
 #
 . "$TEST_DIRECTORY"/lib-httpd.sh
@@ -514,6 +561,27 @@ test_expect_success 'fetch with http:// using protocol v2' '
        grep "git< version 2" log
 '
 
+test_expect_success 'fetch from namespaced repo respects namespaces' '
+       test_when_finished "rm -f log" &&
+
+       git init "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" &&
+       test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" one &&
+       test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" two &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" \
+               update-ref refs/namespaces/ns/refs/heads/master one &&
+
+       GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \
+               fetch "$HTTPD_URL/smart_namespace/nsrepo" \
+               refs/heads/master:refs/heads/theirs &&
+
+       # Server responded using protocol v2
+       grep "fetch< version 2" log &&
+
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" rev-parse one >expect &&
+       git -C http_child rev-parse theirs >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'push with http:// and a config of v2 does not request v2' '
        test_when_finished "rm -f log" &&
        # Till v2 for push is designed, make sure that if a client has
@@ -583,8 +651,8 @@ test_expect_success 'when server does not send "ready", expect FLUSH' '
        test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" git -C http_child \
                -c protocol.version=2 \
                fetch "$HTTPD_URL/one_time_sed/http_parent" 2> err &&
-       grep "fetch< acknowledgments" log &&
-       ! grep "fetch< ready" log &&
+       grep "fetch< .*acknowledgments" log &&
+       ! grep "fetch< .*ready" log &&
        test_i18ngrep "expected no other sections to be sent after no .ready." err
 '
 
index 3f58f05cbb49d3b4a7f50e946e0f46bbc953140a..f87b2f6df329975e243ab9c00b510ee4cc588d0b 100755 (executable)
@@ -208,7 +208,7 @@ test_expect_success 'server is initially ahead - no ref in want' '
        cp -r "$LOCAL_PRISTINE" local &&
        inconsistency master 1234567890123456789012345678901234567890 &&
        test_must_fail git -C local fetch 2>err &&
-       grep "ERR upload-pack: not our ref" err
+       test_i18ngrep "fatal: remote error: upload-pack: not our ref" err
 '
 
 test_expect_success 'server is initially ahead - ref in want' '
@@ -254,7 +254,7 @@ test_expect_success 'server loses a ref - ref in want' '
        echo "s/master/raster/" >"$HTTPD_ROOT_PATH/one-time-sed" &&
        test_must_fail git -C local fetch 2>err &&
 
-       grep "ERR unknown ref refs/heads/raster" err
+       test_i18ngrep "fatal: remote error: unknown ref refs/heads/raster" err
 '
 
 stop_httpd
index ec42c2f7797c38f39bb3420402d975a94217d1f4..da113d975b160f3c34a4acda3d82a26d65f5268e 100755 (executable)
@@ -185,6 +185,10 @@ test_expect_success 'basic colors' '
        test_cmp expect actual
 '
 
+test_expect_success '%S is not a placeholder for rev-list yet' '
+       git rev-list --format="%S" -1 master | grep "%S"
+'
+
 test_expect_success 'advanced colors' '
        cat >expect <<-EOF &&
        commit $head2
index e1cef58f2af96f6185ed93086c3966fb73d58f79..d23b948f27f13ec81dd40fb52caa2ea4b5b8500c 100755 (executable)
@@ -64,15 +64,12 @@ test_expect_success 'merge simple rename+criss-cross with no modifications' '
                git ls-files -u >out &&
                test_line_count = 2 out &&
                git ls-files -o >out &&
-               test_line_count = 3 out &&
+               test_line_count = 1 out &&
 
                git rev-parse >expect       \
-                       L2:three   R2:three \
                        L2:three   R2:three &&
                git rev-parse   >actual     \
                        :2:three   :3:three &&
-               git hash-object >>actual    \
-                       three~HEAD three~R2^0 &&
                test_cmp expect actual
        )
 '
@@ -140,15 +137,12 @@ test_expect_success 'merge criss-cross + rename merges with basic modification'
                git ls-files -u >out &&
                test_line_count = 2 out &&
                git ls-files -o >out &&
-               test_line_count = 3 out &&
+               test_line_count = 1 out &&
 
                git rev-parse >expect       \
-                       L2:three   R2:three \
                        L2:three   R2:three &&
                git rev-parse   >actual     \
                        :2:three   :3:three &&
-               git hash-object >>actual    \
-                       three~HEAD three~R2^0 &&
                test_cmp expect actual
        )
 '
@@ -185,7 +179,7 @@ test_expect_success 'setup differently handled merges of rename/add conflict' '
                git branch B &&
                git checkout -b C &&
                echo 10 >>a &&
-               echo "other content" >>new_a &&
+               test_write_lines 0 1 2 3 4 5 6 7 foobar >new_a &&
                git add a new_a &&
                test_tick && git commit -m C &&
 
@@ -195,14 +189,14 @@ test_expect_success 'setup differently handled merges of rename/add conflict' '
 
                git checkout B^0 &&
                test_must_fail git merge C &&
-               git clean -f &&
+               git show :2:new_a >new_a &&
+               git add new_a &&
                test_tick && git commit -m D &&
                git tag D &&
 
                git checkout C^0 &&
                test_must_fail git merge B &&
-               rm new_a~HEAD new_a &&
-               printf "Incorrectly merged content" >>new_a &&
+               test_write_lines 0 1 2 3 4 5 6 7 bad_merge >new_a &&
                git add -u &&
                test_tick && git commit -m E &&
                git tag E
@@ -225,21 +219,74 @@ test_expect_success 'git detects differently handled merges conflict' '
                test_line_count = 1 out &&
 
                git rev-parse >expect       \
-                       D:new_a  E:new_a &&
+                       C:new_a  D:new_a  E:new_a &&
                git rev-parse   >actual     \
-                       :2:new_a :3:new_a &&
+                       :1:new_a :2:new_a :3:new_a &&
                test_cmp expect actual &&
 
-               git cat-file -p C:new_a >ours &&
-               git cat-file -p B:new_a >theirs &&
+               # Test that the two-way merge in new_a is as expected
+               git cat-file -p D:new_a >ours &&
+               git cat-file -p E:new_a >theirs &&
                >empty &&
                test_must_fail git merge-file \
-                       -L "Temporary merge branch 1" \
+                       -L "HEAD" \
                        -L "" \
-                       -L "Temporary merge branch 2" \
+                       -L "E^0" \
                        ours empty theirs &&
                sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
-               git cat-file -p :1:new_a >actual &&
+               git hash-object new_a >actual &&
+               git hash-object ours  >expect &&
+               test_cmp expect actual
+       )
+'
+
+# Repeat the above testcase with precisely the same setup, other than with
+# the two merge bases having different orderings of commit timestamps so
+# that they are reversed in the order they are provided to merge-recursive,
+# so that we can improve code coverage.
+test_expect_success 'git detects differently handled merges conflict, swapped' '
+       (
+               cd rename-add &&
+
+               # Difference #1: Do cleanup from previous testrun
+               git reset --hard &&
+               git clean -fdqx &&
+
+               # Difference #2: Change commit timestamps
+               btime=$(git log --no-walk --date=raw --format=%cd B | awk "{print \$1}") &&
+               ctime=$(git log --no-walk --date=raw --format=%cd C | awk "{print \$1}") &&
+               newctime=$(($btime+1)) &&
+               git fast-export --no-data --all | sed -e s/$ctime/$newctime/ | git fast-import --force --quiet &&
+               # End of differences; rest is copy-paste of last test
+
+               git checkout D^0 &&
+               test_must_fail git merge -s recursive E^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 3 out &&
+               git ls-files -u >out &&
+               test_line_count = 3 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               git rev-parse >expect       \
+                       C:new_a  D:new_a  E:new_a &&
+               git rev-parse   >actual     \
+                       :1:new_a :2:new_a :3:new_a &&
+               test_cmp expect actual &&
+
+               # Test that the two-way merge in new_a is as expected
+               git cat-file -p D:new_a >ours &&
+               git cat-file -p E:new_a >theirs &&
+               >empty &&
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "E^0" \
+                       ours empty theirs &&
+               sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
+               git hash-object new_a >actual &&
+               git hash-object ours  >expect &&
                test_cmp expect actual
        )
 '
@@ -1402,4 +1449,349 @@ test_expect_failure 'check conflicting modes for regular file' '
        )
 '
 
+# Setup:
+#          L1---L2
+#         /  \ /  \
+#   master    X    ?
+#         \  / \  /
+#          R1---R2
+#
+# Where:
+#   master has two files, named 'b' and 'a'
+#   branches L1 and R1 both modify each of the two files in conflicting ways
+#
+#   L2 is a merge of R1 into L1; more on it later.
+#   R2 is a merge of L1 into R1; more on it later.
+#
+#   X is an auto-generated merge-base used when merging L2 and R2.
+#   since X is a merge of L1 and R1, it has conflicting versions of each file
+#
+#   More about L2 and R2:
+#     - both resolve the conflicts in 'b' and 'a' differently
+#     - L2 renames 'b' to 'm'
+#     - R2 renames 'a' to 'm'
+#
+#   In the end, in file 'm' we have four different conflicting files (from
+#   two versions of 'b' and two of 'a').  In addition, if
+#   merge.conflictstyle is diff3, then the base version also has
+#   conflict markers of its own, leading to a total of three levels of
+#   conflict markers.  This is a pretty weird corner case, but we just want
+#   to ensure that we handle it as well as practical.
+
+test_expect_success 'setup nested conflicts' '
+       test_create_repo nested_conflicts &&
+       (
+               cd nested_conflicts &&
+
+               # Create some related files now
+               for i in $(test_seq 1 10)
+               do
+                       echo Random base content line $i
+               done >initial &&
+
+               cp initial b_L1 &&
+               cp initial b_R1 &&
+               cp initial b_L2 &&
+               cp initial b_R2 &&
+               cp initial a_L1 &&
+               cp initial a_R1 &&
+               cp initial a_L2 &&
+               cp initial a_R2 &&
+
+               test_write_lines b b_L1 >>b_L1 &&
+               test_write_lines b b_R1 >>b_R1 &&
+               test_write_lines b b_L2 >>b_L2 &&
+               test_write_lines b b_R2 >>b_R2 &&
+               test_write_lines a a_L1 >>a_L1 &&
+               test_write_lines a a_R1 >>a_R1 &&
+               test_write_lines a a_L2 >>a_L2 &&
+               test_write_lines a a_R2 >>a_R2 &&
+
+               # Setup original commit (or merge-base), consisting of
+               # files named "b" and "a"
+               cp initial b &&
+               cp initial a &&
+               echo b >>b &&
+               echo a >>a &&
+               git add b a &&
+               test_tick && git commit -m initial &&
+
+               git branch L &&
+               git branch R &&
+
+               # Handle the left side
+               git checkout L &&
+               mv -f b_L1 b &&
+               mv -f a_L1 a &&
+               git add b a &&
+               test_tick && git commit -m "version L1 of files" &&
+               git tag L1 &&
+
+               # Handle the right side
+               git checkout R &&
+               mv -f b_R1 b &&
+               mv -f a_R1 a &&
+               git add b a &&
+               test_tick && git commit -m "verson R1 of files" &&
+               git tag R1 &&
+
+               # Create first merge on left side
+               git checkout L &&
+               test_must_fail git merge R1 &&
+               mv -f b_L2 b &&
+               mv -f a_L2 a &&
+               git add b a &&
+               git mv b m &&
+               test_tick && git commit -m "left merge, rename b->m" &&
+               git tag L2 &&
+
+               # Create first merge on right side
+               git checkout R &&
+               test_must_fail git merge L1 &&
+               mv -f b_R2 b &&
+               mv -f a_R2 a &&
+               git add b a &&
+               git mv a m &&
+               test_tick && git commit -m "right merge, rename a->m" &&
+               git tag R2
+       )
+'
+
+test_expect_success 'check nested conflicts' '
+       (
+               cd nested_conflicts &&
+
+               git clean -f &&
+               git checkout L2^0 &&
+
+               # Merge must fail; there is a conflict
+               test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R2^0 &&
+
+               # Make sure the index has the right number of entries
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 2 out &&
+               # Ensure we have the correct number of untracked files
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               # Create a and b from virtual merge base X
+               git cat-file -p master:a >base &&
+               git cat-file -p L1:a >ours &&
+               git cat-file -p R1:a >theirs &&
+               test_must_fail git merge-file --diff3 \
+                       -L "Temporary merge branch 1" \
+                       -L "merged common ancestors"  \
+                       -L "Temporary merge branch 2" \
+                       ours  \
+                       base  \
+                       theirs &&
+               sed -e "s/^\([<|=>]\)/\1\1/" ours >vmb_a &&
+
+               git cat-file -p master:b >base &&
+               git cat-file -p L1:b >ours &&
+               git cat-file -p R1:b >theirs &&
+               test_must_fail git merge-file --diff3 \
+                       -L "Temporary merge branch 1" \
+                       -L "merged common ancestors"  \
+                       -L "Temporary merge branch 2" \
+                       ours  \
+                       base  \
+                       theirs &&
+               sed -e "s/^\([<|=>]\)/\1\1/" ours >vmb_b &&
+
+               # Compare :2:m to expected values
+               git cat-file -p L2:m >ours &&
+               git cat-file -p R2:b >theirs &&
+               test_must_fail git merge-file --diff3  \
+                       -L "HEAD:m"                    \
+                       -L "merged common ancestors:b" \
+                       -L "R2^0:b"                    \
+                       ours                           \
+                       vmb_b                          \
+                       theirs                         &&
+               sed -e "s/^\([<|=>]\)/\1\1/" ours >m_stage_2 &&
+               git cat-file -p :2:m >actual &&
+               test_cmp m_stage_2 actual &&
+
+               # Compare :3:m to expected values
+               git cat-file -p L2:a >ours &&
+               git cat-file -p R2:m >theirs &&
+               test_must_fail git merge-file --diff3  \
+                       -L "HEAD:a"                    \
+                       -L "merged common ancestors:a" \
+                       -L "R2^0:m"                    \
+                       ours                           \
+                       vmb_a                          \
+                       theirs                         &&
+               sed -e "s/^\([<|=>]\)/\1\1/" ours >m_stage_3 &&
+               git cat-file -p :3:m >actual &&
+               test_cmp m_stage_3 actual &&
+
+               # Compare m to expected contents
+               >empty &&
+               cp m_stage_2 expected_final_m &&
+               test_must_fail git merge-file --diff3 \
+                       -L "HEAD"                     \
+                       -L "merged common ancestors"  \
+                       -L "R2^0"                     \
+                       expected_final_m              \
+                       empty                         \
+                       m_stage_3                     &&
+               test_cmp expected_final_m m
+       )
+'
+
+# Setup:
+#          L1---L2---L3
+#         /  \ /  \ /  \
+#   master    X1   X2   ?
+#         \  / \  / \  /
+#          R1---R2---R3
+#
+# Where:
+#   master has one file named 'content'
+#   branches L1 and R1 both modify each of the two files in conflicting ways
+#
+#   L<n> (n>1) is a merge of R<n-1> into L<n-1>
+#   R<n> (n>1) is a merge of L<n-1> into R<n-1>
+#   L<n> and R<n> resolve the conflicts differently.
+#
+#   X<n> is an auto-generated merge-base used when merging L<n+1> and R<n+1>.
+#   By construction, X1 has conflict markers due to conflicting versions.
+#   X2, due to using merge.conflictstyle=3, has nested conflict markers.
+#
+#   So, merging R3 into L3 using merge.conflictstyle=3 should show the
+#   nested conflict markers from X2 in the base version -- that means we
+#   have three levels of conflict markers.  Can we distinguish all three?
+
+test_expect_success 'setup virtual merge base with nested conflicts' '
+       test_create_repo virtual_merge_base_has_nested_conflicts &&
+       (
+               cd virtual_merge_base_has_nested_conflicts &&
+
+               # Create some related files now
+               for i in $(test_seq 1 10)
+               do
+                       echo Random base content line $i
+               done >content &&
+
+               # Setup original commit
+               git add content &&
+               test_tick && git commit -m initial &&
+
+               git branch L &&
+               git branch R &&
+
+               # Create L1
+               git checkout L &&
+               echo left >>content &&
+               git add content &&
+               test_tick && git commit -m "version L1 of content" &&
+               git tag L1 &&
+
+               # Create R1
+               git checkout R &&
+               echo right >>content &&
+               git add content &&
+               test_tick && git commit -m "verson R1 of content" &&
+               git tag R1 &&
+
+               # Create L2
+               git checkout L &&
+               test_must_fail git -c merge.conflictstyle=diff3 merge R1 &&
+               git checkout L1 content &&
+               test_tick && git commit -m "version L2 of content" &&
+               git tag L2 &&
+
+               # Create R2
+               git checkout R &&
+               test_must_fail git -c merge.conflictstyle=diff3 merge L1 &&
+               git checkout R1 content &&
+               test_tick && git commit -m "version R2 of content" &&
+               git tag R2 &&
+
+               # Create L3
+               git checkout L &&
+               test_must_fail git -c merge.conflictstyle=diff3 merge R2 &&
+               git checkout L1 content &&
+               test_tick && git commit -m "version L3 of content" &&
+               git tag L3 &&
+
+               # Create R3
+               git checkout R &&
+               test_must_fail git -c merge.conflictstyle=diff3 merge L2 &&
+               git checkout R1 content &&
+               test_tick && git commit -m "version R3 of content" &&
+               git tag R3
+       )
+'
+
+test_expect_success 'check virtual merge base with nested conflicts' '
+       (
+               cd virtual_merge_base_has_nested_conflicts &&
+
+               git checkout L3^0 &&
+
+               # Merge must fail; there is a conflict
+               test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R3^0 &&
+
+               # Make sure the index has the right number of entries
+               git ls-files -s >out &&
+               test_line_count = 3 out &&
+               git ls-files -u >out &&
+               test_line_count = 3 out &&
+               # Ensure we have the correct number of untracked files
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               # Compare :[23]:content to expected values
+               git rev-parse L1:content R1:content >expect &&
+               git rev-parse :2:content :3:content >actual &&
+               test_cmp expect actual &&
+
+               # Imitate X1 merge base, except without long enough conflict
+               # markers because a subsequent sed will modify them.  Put
+               # result into vmb.
+               git cat-file -p master:content >base &&
+               git cat-file -p L:content >left &&
+               git cat-file -p R:content >right &&
+               cp left merged-once &&
+               test_must_fail git merge-file --diff3 \
+                       -L "Temporary merge branch 1" \
+                       -L "merged common ancestors"  \
+                       -L "Temporary merge branch 2" \
+                       merged-once \
+                       base        \
+                       right       &&
+               sed -e "s/^\([<|=>]\)/\1\1\1/" merged-once >vmb &&
+
+               # Imitate X2 merge base, overwriting vmb.  Note that we
+               # extend both sets of conflict markers to make them longer
+               # with the sed command.
+               cp left merged-twice &&
+               test_must_fail git merge-file --diff3 \
+                       -L "Temporary merge branch 1" \
+                       -L "merged common ancestors"  \
+                       -L "Temporary merge branch 2" \
+                       merged-twice \
+                       vmb          \
+                       right        &&
+               sed -e "s/^\([<|=>]\)/\1\1\1/" merged-twice >vmb &&
+
+               # Compare :1:content to expected value
+               git cat-file -p :1:content >actual &&
+               test_cmp vmb actual &&
+
+               # Determine expected content in final outer merge, compare to
+               # what the merge generated.
+               cp -f left expect &&
+               test_must_fail git merge-file --diff3                      \
+                       -L "HEAD"  -L "merged common ancestors"  -L "R3^0" \
+                       expect     vmb                           right     &&
+               test_cmp expect content
+       )
+'
+
 test_done
index b97aca7fa263f79520013b14d90fa0b49031484b..09dfa8bd92b51e7a90fcd01f07c62a6fdf65a528 100755 (executable)
@@ -464,17 +464,28 @@ test_expect_success 'handle rename/rename (2to1) conflict correctly' '
                git ls-files -u c >out &&
                test_line_count = 2 out &&
                git ls-files -o >out &&
-               test_line_count = 3 out &&
+               test_line_count = 1 out &&
 
                test_path_is_missing a &&
                test_path_is_missing b &&
-               test_path_is_file c~HEAD &&
-               test_path_is_file c~C^0 &&
 
-               git rev-parse >expect   \
-                       C:a     B:b     &&
-               git hash-object >actual \
-                       c~HEAD  c~C^0   &&
+               git rev-parse >expect  \
+                       C:a     B:b    &&
+               git rev-parse >actual  \
+                       :2:c    :3:c   &&
+               test_cmp expect actual &&
+
+               # Test that the two-way merge in new_a is as expected
+               git cat-file -p :2:c >>ours &&
+               git cat-file -p :3:c >>theirs &&
+               >empty &&
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "C^0" \
+                       ours empty theirs &&
+               git hash-object c >actual &&
+               git hash-object ours >expect &&
                test_cmp expect actual
        )
 '
@@ -673,7 +684,7 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
                git ls-files -u c >out &&
                test_line_count = 2 out &&
                git ls-files -o >out &&
-               test_line_count = 5 out &&
+               test_line_count = 1 out &&
 
                git rev-parse >expect               \
                        A:a   C:b   B:b   C:c   B:c &&
@@ -681,14 +692,27 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
                        :1:a  :2:b  :3:b  :2:c  :3:c &&
                test_cmp expect actual &&
 
-               git rev-parse >expect               \
-                       C:c     B:c     C:b     B:b &&
-               git hash-object >actual                \
-                       c~HEAD  c~B\^0  b~HEAD  b~B\^0 &&
-               test_cmp expect actual &&
+               # Record some contents for re-doing merges
+               git cat-file -p A:a >stuff &&
+               git cat-file -p C:b >important_info &&
+               git cat-file -p B:c >precious_data &&
+               >empty &&
 
-               test_path_is_missing b &&
-               test_path_is_missing c
+               # Test the merge in b
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "B^0" \
+                       important_info empty stuff &&
+               test_cmp important_info b &&
+
+               # Test the merge in c
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "B^0" \
+                       stuff empty precious_data &&
+               test_cmp stuff c
        )
 '
 
@@ -937,4 +961,283 @@ test_expect_failure 'mod6-check: chains of rename/rename(1to2) and rename/rename
        )
 '
 
+test_conflicts_with_adds_and_renames() {
+       sideL=$1
+       sideR=$2
+
+       # Setup:
+       #          L
+       #         / \
+       #   master   ?
+       #         \ /
+       #          R
+       #
+       # Where:
+       #   Both L and R have files named 'three' which collide.  Each of
+       #   the colliding files could have been involved in a rename, in
+       #   which case there was a file named 'one' or 'two' that was
+       #   modified on the opposite side of history and renamed into the
+       #   collision on this side of history.
+       #
+       # Questions:
+       #   1) The index should contain both a stage 2 and stage 3 entry
+       #      for the colliding file.  Does it?
+       #   2) When renames are involved, the content merges are clean, so
+       #      the index should reflect the content merges, not merely the
+       #      version of the colliding file from the prior commit.  Does
+       #      it?
+       #   3) There should be a file in the worktree named 'three'
+       #      containing the two-way merged contents of the content-merged
+       #      versions of 'three' from each of the two colliding
+       #      files.  Is it present?
+       #   4) There should not be any three~* files in the working
+       #      tree
+       test_expect_success "setup simple $sideL/$sideR conflict" '
+               test_create_repo simple_${sideL}_${sideR} &&
+               (
+                       cd simple_${sideL}_${sideR} &&
+
+                       # Create some related files now
+                       for i in $(test_seq 1 10)
+                       do
+                               echo Random base content line $i
+                       done >file_v1 &&
+                       cp file_v1 file_v2 &&
+                       echo modification >>file_v2 &&
+
+                       cp file_v1 file_v3 &&
+                       echo more stuff >>file_v3 &&
+                       cp file_v3 file_v4 &&
+                       echo yet more stuff >>file_v4 &&
+
+                       # Use a tag to record both these files for simple
+                       # access, and clean out these untracked files
+                       git tag file_v1 $(git hash-object -w file_v1) &&
+                       git tag file_v2 $(git hash-object -w file_v2) &&
+                       git tag file_v3 $(git hash-object -w file_v3) &&
+                       git tag file_v4 $(git hash-object -w file_v4) &&
+                       git clean -f &&
+
+                       # Setup original commit (or merge-base), consisting of
+                       # files named "one" and "two" if renames were involved.
+                       touch irrelevant_file &&
+                       git add irrelevant_file &&
+                       if [ $sideL = "rename" ]
+                       then
+                               git show file_v1 >one &&
+                               git add one
+                       fi &&
+                       if [ $sideR = "rename" ]
+                       then
+                               git show file_v3 >two &&
+                               git add two
+                       fi &&
+                       test_tick && git commit -m initial &&
+
+                       git branch L &&
+                       git branch R &&
+
+                       # Handle the left side
+                       git checkout L &&
+                       if [ $sideL = "rename" ]
+                       then
+                               git mv one three
+                       else
+                               git show file_v2 >three &&
+                               git add three
+                       fi &&
+                       if [ $sideR = "rename" ]
+                       then
+                               git show file_v4 >two &&
+                               git add two
+                       fi &&
+                       test_tick && git commit -m L &&
+
+                       # Handle the right side
+                       git checkout R &&
+                       if [ $sideL = "rename" ]
+                       then
+                               git show file_v2 >one &&
+                               git add one
+                       fi &&
+                       if [ $sideR = "rename" ]
+                       then
+                               git mv two three
+                       else
+                               git show file_v4 >three &&
+                               git add three
+                       fi &&
+                       test_tick && git commit -m R
+               )
+       '
+
+       test_expect_success "check simple $sideL/$sideR conflict" '
+               (
+                       cd simple_${sideL}_${sideR} &&
+
+                       git checkout L^0 &&
+
+                       # Merge must fail; there is a conflict
+                       test_must_fail git merge -s recursive R^0 &&
+
+                       # Make sure the index has the right number of entries
+                       git ls-files -s >out &&
+                       test_line_count = 3 out &&
+                       git ls-files -u >out &&
+                       test_line_count = 2 out &&
+                       # Ensure we have the correct number of untracked files
+                       git ls-files -o >out &&
+                       test_line_count = 1 out &&
+
+                       # Nothing should have touched irrelevant_file
+                       git rev-parse >actual      \
+                               :0:irrelevant_file \
+                               :2:three           \
+                               :3:three           &&
+                       git rev-parse >expected        \
+                               master:irrelevant_file \
+                               file_v2                \
+                               file_v4                &&
+                       test_cmp expected actual &&
+
+                       # Make sure we have the correct merged contents for
+                       # three
+                       git show file_v1 >expected &&
+                       cat <<-\EOF >>expected &&
+                       <<<<<<< HEAD
+                       modification
+                       =======
+                       more stuff
+                       yet more stuff
+                       >>>>>>> R^0
+                       EOF
+
+                       test_cmp expected three
+               )
+       '
+}
+
+test_conflicts_with_adds_and_renames rename rename
+test_conflicts_with_adds_and_renames rename add
+test_conflicts_with_adds_and_renames add    rename
+test_conflicts_with_adds_and_renames add    add
+
+# Setup:
+#          L
+#         / \
+#   master   ?
+#         \ /
+#          R
+#
+# Where:
+#   master has two files, named 'one' and 'two'.
+#   branches L and R both modify 'one', in conflicting ways.
+#   branches L and R both modify 'two', in conflicting ways.
+#   branch L also renames 'one' to 'three'.
+#   branch R also renames 'two' to 'three'.
+#
+#   So, we have four different conflicting files that all end up at path
+#   'three'.
+test_expect_success 'setup nested conflicts from rename/rename(2to1)' '
+       test_create_repo nested_conflicts_from_rename_rename &&
+       (
+               cd nested_conflicts_from_rename_rename &&
+
+               # Create some related files now
+               for i in $(test_seq 1 10)
+               do
+                       echo Random base content line $i
+               done >file_v1 &&
+
+               cp file_v1 file_v2 &&
+               cp file_v1 file_v3 &&
+               cp file_v1 file_v4 &&
+               cp file_v1 file_v5 &&
+               cp file_v1 file_v6 &&
+
+               echo one  >>file_v1 &&
+               echo uno  >>file_v2 &&
+               echo eins >>file_v3 &&
+
+               echo two  >>file_v4 &&
+               echo dos  >>file_v5 &&
+               echo zwei >>file_v6 &&
+
+               # Setup original commit (or merge-base), consisting of
+               # files named "one" and "two".
+               mv file_v1 one &&
+               mv file_v4 two &&
+               git add one two &&
+               test_tick && git commit -m english &&
+
+               git branch L &&
+               git branch R &&
+
+               # Handle the left side
+               git checkout L &&
+               git rm one two &&
+               mv -f file_v2 three &&
+               mv -f file_v5 two &&
+               git add two three &&
+               test_tick && git commit -m spanish &&
+
+               # Handle the right side
+               git checkout R &&
+               git rm one two &&
+               mv -f file_v3 one &&
+               mv -f file_v6 three &&
+               git add one three &&
+               test_tick && git commit -m german
+       )
+'
+
+test_expect_success 'check nested conflicts from rename/rename(2to1)' '
+       (
+               cd nested_conflicts_from_rename_rename &&
+
+               git checkout L^0 &&
+
+               # Merge must fail; there is a conflict
+               test_must_fail git merge -s recursive R^0 &&
+
+               # Make sure the index has the right number of entries
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 2 out &&
+               # Ensure we have the correct number of untracked files
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               # Compare :2:three to expected values
+               git cat-file -p master:one >base &&
+               git cat-file -p L:three >ours &&
+               git cat-file -p R:one >theirs &&
+               test_must_fail git merge-file    \
+                       -L "HEAD:three"  -L ""  -L "R^0:one" \
+                       ours             base   theirs &&
+               sed -e "s/^\([<=>]\)/\1\1/" ours >L-three &&
+               git cat-file -p :2:three >expect &&
+               test_cmp expect L-three &&
+
+               # Compare :2:three to expected values
+               git cat-file -p master:two >base &&
+               git cat-file -p L:two >ours &&
+               git cat-file -p R:three >theirs &&
+               test_must_fail git merge-file    \
+                       -L "HEAD:two"  -L ""  -L "R^0:three" \
+                       ours           base   theirs &&
+               sed -e "s/^\([<=>]\)/\1\1/" ours >R-three &&
+               git cat-file -p :3:three >expect &&
+               test_cmp expect R-three &&
+
+               # Compare three to expected contents
+               >empty &&
+               test_must_fail git merge-file    \
+                       -L "HEAD"  -L ""  -L "R^0" \
+                       L-three    empty  R-three &&
+               test_cmp three L-three
+       )
+'
+
 test_done
index 4a71f17edd69a2cb6962cf759aa6ad1b7604c2db..62c564707b9ff8a8e86b1f8d2d2e3919c36f1b5f 100755 (executable)
@@ -278,7 +278,7 @@ test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) con
                git ls-files -u >out &&
                test_line_count = 2 out &&
                git ls-files -o >out &&
-               test_line_count = 3 out &&
+               test_line_count = 1 out &&
 
                git rev-parse >actual \
                        :0:x/b :0:x/c :0:x/d :0:x/e :0:x/m :0:x/n &&
@@ -293,15 +293,16 @@ test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) con
                         A:y/wham  B:z/wham &&
                test_cmp expect actual &&
 
-               test_path_is_missing x/wham &&
-               test_path_is_file x/wham~HEAD &&
-               test_path_is_file x/wham~B^0 &&
-
-               git hash-object >actual \
-                       x/wham~HEAD x/wham~B^0 &&
-               git rev-parse >expect \
-                       A:y/wham    B:z/wham &&
-               test_cmp expect actual
+               # Test that the two-way merge in x/wham is as expected
+               git cat-file -p :2:x/wham >expect &&
+               git cat-file -p :3:x/wham >other &&
+               >empty &&
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "B^0" \
+                       expect empty other &&
+               test_cmp expect x/wham
        )
 '
 
@@ -1077,7 +1078,7 @@ test_expect_success '5c-check: Transitive rename would cause rename/rename/renam
                git ls-files -u >out &&
                test_line_count = 6 out &&
                git ls-files -o >out &&
-               test_line_count = 3 out &&
+               test_line_count = 1 out &&
 
                git rev-parse >actual \
                        :0:y/b :0:y/c :0:y/e &&
@@ -1093,9 +1094,9 @@ test_expect_success '5c-check: Transitive rename would cause rename/rename/renam
                test_cmp expect actual &&
 
                git hash-object >actual \
-                       w/d~HEAD w/d~B^0 z/d &&
+                       z/d &&
                git rev-parse >expect \
-                       O:x/d    B:w/d   O:x/d &&
+                       O:x/d &&
                test_cmp expect actual &&
                test_path_is_missing x/d &&
                test_path_is_file y/d &&
@@ -1670,7 +1671,7 @@ test_expect_success '7b-check: rename/rename(2to1), but only due to transitive r
                git ls-files -u >out &&
                test_line_count = 2 out &&
                git ls-files -o >out &&
-               test_line_count = 3 out &&
+               test_line_count = 1 out &&
 
                git rev-parse >actual \
                        :0:y/b :0:y/c :2:y/d :3:y/d &&
@@ -1678,15 +1679,16 @@ test_expect_success '7b-check: rename/rename(2to1), but only due to transitive r
                         O:z/b  O:z/c  O:w/d  O:x/d &&
                test_cmp expect actual &&
 
-               test_path_is_missing y/d &&
-               test_path_is_file y/d~HEAD &&
-               test_path_is_file y/d~B^0 &&
-
-               git hash-object >actual \
-                       y/d~HEAD y/d~B^0 &&
-               git rev-parse >expect \
-                       O:w/d    O:x/d &&
-               test_cmp expect actual
+               # Test that the two-way merge in y/d is as expected
+               git cat-file -p :2:y/d >expect &&
+               git cat-file -p :3:y/d >other &&
+               >empty &&
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "B^0" \
+                       expect empty other &&
+               test_cmp expect y/d
        )
 '
 
@@ -3161,11 +3163,48 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)
        )
 '
 
+test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2), other direction' '
+       (
+               cd 10c &&
+
+               git reset --hard &&
+               git clean -fdqx &&
+
+               git checkout B^0 &&
+               mkdir y &&
+               echo important >y/c &&
+
+               test_must_fail git merge -s recursive A^0 >out 2>err &&
+               test_i18ngrep "CONFLICT (rename/rename)" out &&
+               test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
+
+               git ls-files -s >out &&
+               test_line_count = 6 out &&
+               git ls-files -u >out &&
+               test_line_count = 3 out &&
+               git ls-files -o >out &&
+               test_line_count = 3 out &&
+
+               git rev-parse >actual \
+                       :0:y/a :0:y/b :0:x/d :1:x/c :3:w/c :2:y/c &&
+               git rev-parse >expect \
+                        O:z/a  O:z/b  O:x/d  O:x/c  O:x/c  O:x/c &&
+               test_cmp expect actual &&
+
+               git hash-object y/c~HEAD >actual &&
+               git rev-parse O:x/c >expect &&
+               test_cmp expect actual &&
+
+               echo important >expect &&
+               test_cmp expect y/c
+       )
+'
+
 # Testcase 10d, Delete untracked w/ dir rename/rename(2to1)
 #   Commit O: z/{a,b,c_1},        x/{d,e,f_2}
 #   Commit A: y/{a,b},            x/{d,e,f_2,wham_1} + untracked y/wham
 #   Commit B: z/{a,b,c_1,wham_2}, y/{d,e}
-#   Expected: Failed Merge; y/{a,b,d,e} + untracked y/{wham,wham~B^0,wham~HEAD}+
+#   Expected: Failed Merge; y/{a,b,d,e} + untracked y/{wham,wham~merged}+
 #             CONFLICT(rename/rename) z/c_1 vs x/f_2 -> y/wham
 #             ERROR_MSG(Refusing to lose untracked file at y/wham)
 
@@ -3219,7 +3258,7 @@ test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)' '
                git ls-files -u >out &&
                test_line_count = 2 out &&
                git ls-files -o >out &&
-               test_line_count = 4 out &&
+               test_line_count = 3 out &&
 
                git rev-parse >actual \
                        :0:y/a :0:y/b :0:y/d :0:y/e :2:y/wham :3:y/wham &&
@@ -3232,11 +3271,16 @@ test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)' '
                echo important >expect &&
                test_cmp expect y/wham &&
 
-               git hash-object >actual \
-                       y/wham~B^0 y/wham~HEAD &&
-               git rev-parse >expect \
-                       O:x/f      O:z/c &&
-               test_cmp expect actual
+               # Test that the two-way merge in y/wham~merged is as expected
+               git cat-file -p :2:y/wham >expect &&
+               git cat-file -p :3:y/wham >other &&
+               >empty &&
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "B^0" \
+                       expect empty other &&
+               test_cmp expect y/wham~merged
        )
 '
 
@@ -3665,7 +3709,7 @@ test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rena
                git ls-files -u >out &&
                test_line_count = 4 out &&
                git ls-files -o >out &&
-               test_line_count = 4 out &&
+               test_line_count = 3 out &&
 
                echo different >expected &&
                echo mods >>expected &&
@@ -3677,11 +3721,17 @@ test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rena
                         O:z/a  O:z/b  O:x/d  O:x/c  O:x/c  A:y/c  O:x/c &&
                test_cmp expect actual &&
 
-               git hash-object >actual \
-                       y/c~B^0 y/c~HEAD &&
-               git rev-parse >expect \
-                       O:x/c   A:y/c &&
-               test_cmp expect actual
+               # See if y/c~merged has expected contents; requires manually
+               # doing the expected file merge
+               git cat-file -p A:y/c >c1 &&
+               git cat-file -p B:z/c >c2 &&
+               >empty &&
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "B^0" \
+                       c1 empty c2 &&
+               test_cmp c1 y/c~merged
        )
 '
 
@@ -3689,7 +3739,7 @@ test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rena
 #   Commit O: z/{a,b},        x/{c_1,d_2}
 #   Commit A: y/{a,b,wham_1}, x/d_2, except y/wham has uncommitted mods
 #   Commit B: z/{a,b,wham_2}, x/c_1
-#   Expected: Failed Merge; y/{a,b} + untracked y/{wham~B^0,wham~B^HEAD} +
+#   Expected: Failed Merge; y/{a,b} + untracked y/{wham~merged} +
 #             y/wham with dirty changes from before merge +
 #             CONFLICT(rename/rename) x/c vs x/d -> y/wham
 #             ERROR_MSG(Refusing to lose dirty file at y/wham)
@@ -3741,24 +3791,30 @@ test_expect_success '11f-check: Avoid deleting not-uptodate with dir rename/rena
                git ls-files -u >out &&
                test_line_count = 2 out &&
                git ls-files -o >out &&
-               test_line_count = 4 out &&
+               test_line_count = 3 out &&
 
                test_seq 1 10 >expected &&
                echo important >>expected &&
                test_cmp expected y/wham &&
 
                test_must_fail git rev-parse :1:y/wham &&
-               git hash-object >actual \
-                       y/wham~B^0 y/wham~HEAD &&
-               git rev-parse >expect \
-                       O:x/d      O:x/c &&
-               test_cmp expect actual &&
 
                git rev-parse >actual \
                        :0:y/a :0:y/b :2:y/wham :3:y/wham &&
                git rev-parse >expect \
                         O:z/a  O:z/b  O:x/c     O:x/d &&
-               test_cmp expect actual
+               test_cmp expect actual &&
+
+               # Test that the two-way merge in y/wham~merged is as expected
+               git cat-file -p :2:y/wham >expect &&
+               git cat-file -p :3:y/wham >other &&
+               >empty &&
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "B^0" \
+                       expect empty other &&
+               test_cmp expect y/wham~merged
        )
 '
 
index 5d6d3184ac390f2cd476a322cf6d15e69a912494..d638119750c5b9f11682814de466cc19a9d38770 100755 (executable)
@@ -133,8 +133,8 @@ test_expect_success 'tag replaced commit' '
 
 test_expect_success '"git fsck" works' '
      git fsck master >fsck_master.out &&
-     grep "dangling commit $R" fsck_master.out &&
-     grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
+     test_i18ngrep "dangling commit $R" fsck_master.out &&
+     test_i18ngrep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
      test -z "$(git fsck)"
 '
 
index eb32505a6ef8804f4f7762ce0332f00beeacbe99..9c114277196e5c231869dbfe51f88760c40ae0b8 100755 (executable)
@@ -283,7 +283,7 @@ test_expect_success 'verify tree:0 includes trees in "filtered" output' '
 
 # Make sure tree:0 does not iterate through any trees.
 
-test_expect_success 'filter a GIANT tree through tree:0' '
+test_expect_success 'verify skipping tree iteration when not collecting omits' '
        GIT_TRACE=1 git -C r3 rev-list \
                --objects --filter=tree:0 HEAD 2>filter_trace &&
        grep "Skipping contents of tree [.][.][.]" filter_trace >actual &&
@@ -294,6 +294,126 @@ test_expect_success 'filter a GIANT tree through tree:0' '
        ! grep "Skipping contents of tree [^.]" filter_trace
 '
 
+# Test tree:# filters.
+
+expect_has () {
+       commit=$1 &&
+       name=$2 &&
+
+       hash=$(git -C r3 rev-parse $commit:$name) &&
+       grep "^$hash $name$" actual
+}
+
+test_expect_success 'verify tree:1 includes root trees' '
+       git -C r3 rev-list --objects --filter=tree:1 HEAD >actual &&
+
+       # We should get two root directories and two commits.
+       expect_has HEAD "" &&
+       expect_has HEAD~1 ""  &&
+       test_line_count = 4 actual
+'
+
+test_expect_success 'verify tree:2 includes root trees and immediate children' '
+       git -C r3 rev-list --objects --filter=tree:2 HEAD >actual &&
+
+       expect_has HEAD "" &&
+       expect_has HEAD~1 "" &&
+       expect_has HEAD dir1 &&
+       expect_has HEAD pattern &&
+       expect_has HEAD sparse1 &&
+       expect_has HEAD sparse2 &&
+
+       # There are also 2 commit objects
+       test_line_count = 8 actual
+'
+
+test_expect_success 'verify tree:3 includes everything expected' '
+       git -C r3 rev-list --objects --filter=tree:3 HEAD >actual &&
+
+       expect_has HEAD "" &&
+       expect_has HEAD~1 "" &&
+       expect_has HEAD dir1 &&
+       expect_has HEAD dir1/sparse1 &&
+       expect_has HEAD dir1/sparse2 &&
+       expect_has HEAD pattern &&
+       expect_has HEAD sparse1 &&
+       expect_has HEAD sparse2 &&
+
+       # There are also 2 commit objects
+       test_line_count = 10 actual
+'
+
+# Test provisional omit collection logic with a repo that has objects appearing
+# at multiple depths - first deeper than the filter's threshold, then shallow.
+
+test_expect_success 'setup r4' '
+       git init r4 &&
+
+       echo foo > r4/foo &&
+       mkdir r4/subdir &&
+       echo bar > r4/subdir/bar &&
+
+       mkdir r4/filt &&
+       cp -r r4/foo r4/subdir r4/filt &&
+
+       git -C r4 add foo subdir filt &&
+       git -C r4 commit -m "commit msg"
+'
+
+expect_has_with_different_name () {
+       repo=$1 &&
+       name=$2 &&
+
+       hash=$(git -C $repo rev-parse HEAD:$name) &&
+       ! grep "^$hash $name$" actual &&
+       grep "^$hash " actual &&
+       ! grep "~$hash" actual
+}
+
+test_expect_success 'test tree:# filter provisional omit for blob and tree' '
+       git -C r4 rev-list --objects --filter-print-omitted --filter=tree:2 \
+               HEAD >actual &&
+       expect_has_with_different_name r4 filt/foo &&
+       expect_has_with_different_name r4 filt/subdir
+'
+
+test_expect_success 'verify skipping tree iteration when collecting omits' '
+       GIT_TRACE=1 git -C r4 rev-list --filter-print-omitted \
+               --objects --filter=tree:0 HEAD 2>filter_trace &&
+       grep "^Skipping contents of tree " filter_trace >actual &&
+
+       echo "Skipping contents of tree subdir/..." >expect &&
+       test_cmp expect actual
+'
+
+# Test tree:<depth> where a tree is iterated to twice - once where a subentry is
+# too deep to be included, and again where the blob inside it is shallow enough
+# to be included. This makes sure we don't use LOFR_MARK_SEEN incorrectly (we
+# can't use it because a tree can be iterated over again at a lower depth).
+
+test_expect_success 'tree:<depth> where we iterate over tree at two levels' '
+       git init r5 &&
+
+       mkdir -p r5/a/subdir/b &&
+       echo foo > r5/a/subdir/b/foo &&
+
+       mkdir -p r5/subdir/b &&
+       echo foo > r5/subdir/b/foo &&
+
+       git -C r5 add a subdir &&
+       git -C r5 commit -m "commit msg" &&
+
+       git -C r5 rev-list --objects --filter=tree:4 HEAD >actual &&
+       expect_has_with_different_name r5 a/subdir/b/foo
+'
+
+test_expect_success 'tree:<depth> which filters out blob but given as arg' '
+       blob_hash=$(git -C r4 rev-parse HEAD:subdir/bar) &&
+
+       git -C r4 rev-list --objects --filter=tree:1 HEAD $blob_hash >actual &&
+       grep ^$blob_hash actual
+'
+
 # Delete some loose objects and use rev-list, but WITHOUT any filtering.
 # This models previously omitted objects that we did not receive.
 
@@ -324,4 +444,21 @@ test_expect_success 'rev-list W/ missing=allow-any' '
        git -C r1 rev-list --quiet --missing=allow-any --objects HEAD
 '
 
+# Test expansion of filter specs.
+
+test_expect_success 'expand blob limit in protocol' '
+       git -C r2 config --local uploadpack.allowfilter 1 &&
+       GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 clone \
+               --filter=blob:limit=1k "file://$(pwd)/r2" limit &&
+       ! grep "blob:limit=1k" trace &&
+       grep "blob:limit=1024" trace
+'
+
+test_expect_success 'expand tree depth limit in protocol' '
+       GIT_TRACE_PACKET="$(pwd)/tree_trace" git -c protocol.version=2 clone \
+               --filter=tree:0k "file://$(pwd)/r2" tree &&
+       ! grep "tree:0k" tree_trace &&
+       grep "tree:0" tree_trace
+'
+
 test_done
index e436a7396241e6aad71a5f62500ffe0b9d7c0b7e..457cc167c774a0e4551cfc98c4f9aec6a17e1416 100755 (executable)
@@ -31,7 +31,7 @@ test_expect_success 'setup a tree' '
        mkdir sub &&
        while read path
        do
-               : >$path &&
+               echo content >$path &&
                git add $path || return 1
        done <expect &&
        git commit -m "initial commit" &&
@@ -48,6 +48,10 @@ test_expect_success 'pathspec with labels and non existent .gitattributes' '
        test_must_be_empty actual
 '
 
+test_expect_success 'pathspec with labels and non existent .gitattributes (2)' '
+       test_must_fail git grep content HEAD -- ":(attr:label)"
+'
+
 test_expect_success 'setup .gitattributes' '
        cat <<-\EOF >.gitattributes &&
        fileA labelA
@@ -74,6 +78,15 @@ test_expect_success 'check specific set attr' '
        test_cmp expect actual
 '
 
+test_expect_success 'check specific set attr (2)' '
+       cat <<-\EOF >expect &&
+       HEAD:fileSetLabel
+       HEAD:sub/fileSetLabel
+       EOF
+       git grep -l content HEAD ":(attr:label)" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'check specific unset attr' '
        cat <<-\EOF >expect &&
        fileUnsetLabel
@@ -83,6 +96,15 @@ test_expect_success 'check specific unset attr' '
        test_cmp expect actual
 '
 
+test_expect_success 'check specific unset attr (2)' '
+       cat <<-\EOF >expect &&
+       HEAD:fileUnsetLabel
+       HEAD:sub/fileUnsetLabel
+       EOF
+       git grep -l content HEAD ":(attr:-label)" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'check specific value attr' '
        cat <<-\EOF >expect &&
        fileValue
@@ -94,6 +116,16 @@ test_expect_success 'check specific value attr' '
        test_must_be_empty actual
 '
 
+test_expect_success 'check specific value attr (2)' '
+       cat <<-\EOF >expect &&
+       HEAD:fileValue
+       HEAD:sub/fileValue
+       EOF
+       git grep -l content HEAD ":(attr:label=foo)" >actual &&
+       test_cmp expect actual &&
+       test_must_fail git grep -l content HEAD ":(attr:label=bar)"
+'
+
 test_expect_success 'check unspecified attr' '
        cat <<-\EOF >expect &&
        .gitattributes
@@ -118,6 +150,30 @@ test_expect_success 'check unspecified attr' '
        test_cmp expect actual
 '
 
+test_expect_success 'check unspecified attr (2)' '
+       cat <<-\EOF >expect &&
+       HEAD:.gitattributes
+       HEAD:fileA
+       HEAD:fileAB
+       HEAD:fileAC
+       HEAD:fileB
+       HEAD:fileBC
+       HEAD:fileC
+       HEAD:fileNoLabel
+       HEAD:fileWrongLabel
+       HEAD:sub/fileA
+       HEAD:sub/fileAB
+       HEAD:sub/fileAC
+       HEAD:sub/fileB
+       HEAD:sub/fileBC
+       HEAD:sub/fileC
+       HEAD:sub/fileNoLabel
+       HEAD:sub/fileWrongLabel
+       EOF
+       git grep -l ^ HEAD ":(attr:!label)" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'check multiple unspecified attr' '
        cat <<-\EOF >expect &&
        .gitattributes
index 97bfbee6e8d69e46bd1ef1c94dae32d64be977b2..0ffd63071392eba0cd960ddf50d45bc0b5f1d5ea 100755 (executable)
@@ -83,6 +83,8 @@ test_atom head push:strip=1 remotes/myfork/master
 test_atom head push:strip=-1 master
 test_atom head objecttype commit
 test_atom head objectsize 171
+test_atom head objectsize:disk 138
+test_atom head deltabase 0000000000000000000000000000000000000000
 test_atom head objectname $(git rev-parse refs/heads/master)
 test_atom head objectname:short $(git rev-parse --short refs/heads/master)
 test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)
@@ -124,6 +126,10 @@ test_atom tag upstream ''
 test_atom tag push ''
 test_atom tag objecttype tag
 test_atom tag objectsize 154
+test_atom tag objectsize:disk 138
+test_atom tag '*objectsize:disk' 138
+test_atom tag deltabase 0000000000000000000000000000000000000000
+test_atom tag '*deltabase' 0000000000000000000000000000000000000000
 test_atom tag objectname $(git rev-parse refs/tags/testtag)
 test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)
 test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)
index 76a7cb0af7955aa618556a43cc9f2a568d8de3e8..aba2d4d6ee415ab5528ed67d518cfb26412f1142 100755 (executable)
@@ -984,6 +984,11 @@ test_expect_success 'submodule deinit should remove the whole submodule section
        rmdir init
 '
 
+test_expect_success 'submodule deinit should unset core.worktree' '
+       test_path_is_file .git/modules/example/config &&
+       test_must_fail git config -f .git/modules/example/config core.worktree
+'
+
 test_expect_success 'submodule deinit from subdirectory' '
        git submodule update --init &&
        git config submodule.example.foo bar &&
index ce74c12da2daccd26edbb92070e5ff4eb1f09f9c..1cfa150768d7f813ba9943b4bd17887469cb1c04 100755 (executable)
@@ -75,7 +75,12 @@ test_expect_success 're-setup nested submodule' '
        GIT_WORK_TREE=../../../nested git -C sub1/.git/modules/nested config \
                core.worktree "../../../nested" &&
        # make sure this re-setup is correct
-       git status --ignore-submodules=none
+       git status --ignore-submodules=none &&
+
+       # also make sure this old setup does not regress
+       git submodule update --init --recursive >out 2>err &&
+       test_must_be_empty out &&
+       test_must_be_empty err
 '
 
 test_expect_success 'absorb the git dir in a nested submodule' '
index 293e2e1963962e49afa10882439b5106607439a1..49a37efe9c1d12567dab3997bd8307896ccaa01c 100755 (executable)
@@ -154,7 +154,7 @@ test_expect_success 'fsck detects symlinked .gitmodules file' '
                # symlink detector; this grep string comes from the config
                # variable name and will not be translated.
                test_must_fail git fsck 2>output &&
-               grep gitmodulesSymlink output
+               test_i18ngrep gitmodulesSymlink output
        )
 '
 
@@ -172,7 +172,7 @@ test_expect_success 'fsck detects non-blob .gitmodules' '
                git ls-tree HEAD | sed s/subdir/.gitmodules/ | git mktree &&
 
                test_must_fail git fsck 2>output &&
-               grep gitmodulesBlob output
+               test_i18ngrep gitmodulesBlob output
        )
 '
 
@@ -186,7 +186,7 @@ test_expect_success 'fsck detects corrupt .gitmodules' '
                git commit -m "broken gitmodules" &&
 
                git fsck 2>output &&
-               grep gitmodulesParse output &&
+               test_i18ngrep gitmodulesParse output &&
                test_i18ngrep ! "bad config" output
        )
 '
index ebfcad9c4c7d3c6766907d76c5e0c794a0720e1c..ba8bd1b51497df1aedf2280210a98b389f12b9e7 100755 (executable)
@@ -215,7 +215,7 @@ test_expect_success 'with hook and editor (merge)' '
 test_rebase () {
        expect=$1 &&
        mode=$2 &&
-       test_expect_$expect C_LOCALE_OUTPUT "with hook (rebase $mode)" '
+       test_expect_$expect C_LOCALE_OUTPUT "with hook (rebase ${mode:--i})" '
                test_when_finished "\
                        git rebase --abort
                        git checkout -f master
@@ -225,7 +225,7 @@ test_rebase () {
                GIT_EDITOR="\"$FAKE_EDITOR\"" &&
                (
                        export GIT_SEQUENCE_EDITOR GIT_EDITOR &&
-                       test_must_fail git rebase $mode b &&
+                       test_must_fail git rebase -i $mode b &&
                        echo x >a &&
                        git add a &&
                        test_must_fail git rebase --continue &&
@@ -241,18 +241,18 @@ test_rebase () {
                        git add b &&
                        git rebase --continue
                ) &&
-               if test $mode = -p # reword amended after pick
+               if test "$mode" = -p # reword amended after pick
                then
                        n=18
                else
                        n=17
                fi &&
                git log --pretty=%s -g -n$n HEAD@{1} >actual &&
-               test_cmp "$TEST_DIRECTORY/t7505/expected-rebase$mode" actual
+               test_cmp "$TEST_DIRECTORY/t7505/expected-rebase${mode:--i}" actual
        '
 }
 
-test_rebase success -i
+test_rebase success
 test_have_prereq !REBASE_P || test_rebase success -p
 
 test_expect_success 'with hook (cherry-pick)' '
index 6a392e87bcc17712961c548c0ee84e11269aef0f..5690fe28106624f3d666ccdda1dbcdd58673fb13 100755 (executable)
@@ -66,6 +66,34 @@ test_expect_success 'fast-export master~2..master' '
 
 '
 
+test_expect_success 'fast-export --reference-excluded-parents master~2..master' '
+
+       git fast-export --reference-excluded-parents master~2..master >actual &&
+       grep commit.refs/heads/master actual >commit-count &&
+       test_line_count = 2 commit-count &&
+       sed "s/master/rewrite/" actual |
+               (cd new &&
+                git fast-import &&
+                test $MASTER = $(git rev-parse --verify refs/heads/rewrite))
+'
+
+test_expect_success 'fast-export --show-original-ids' '
+
+       git fast-export --show-original-ids master >output &&
+       grep ^original-oid output| sed -e s/^original-oid.// | sort >actual &&
+       git rev-list --objects master muss >objects-and-names &&
+       awk "{print \$1}" objects-and-names | sort >commits-trees-blobs &&
+       comm -23 actual commits-trees-blobs >unfound &&
+       test_must_be_empty unfound
+'
+
+test_expect_success 'fast-export --show-original-ids | git fast-import' '
+
+       git fast-export --show-original-ids master muss | git fast-import --quiet &&
+       test $MASTER = $(git rev-parse --verify refs/heads/master) &&
+       test $MUSS = $(git rev-parse --verify refs/tags/muss)
+'
+
 test_expect_success 'iso-8859-1' '
 
        git config i18n.commitencoding ISO8859-1 &&
@@ -325,6 +353,22 @@ test_expect_success 'rewriting tag of filtered out object' '
 )
 '
 
+test_expect_success 'rewrite tag predating pathspecs to nothing' '
+       test_create_repo rewrite_tag_predating_pathspecs &&
+       (
+               cd rewrite_tag_predating_pathspecs &&
+
+               test_commit initial &&
+
+               git tag -a -m "Some old tag" v0.0.0.0.0.0.1 &&
+
+               test_commit bar &&
+
+               git fast-export --tag-of-filtered-object=rewrite --all -- bar.t >output &&
+               grep from.$ZERO_OID output
+       )
+'
+
 cat > limit-by-paths/expected << EOF
 blob
 mark :1
@@ -366,6 +410,26 @@ test_expect_success 'path limiting with import-marks does not lose unmodified fi
        grep file0 actual
 '
 
+test_expect_success 'avoid corrupt stream with non-existent mark' '
+       test_create_repo avoid_non_existent_mark &&
+       (
+               cd avoid_non_existent_mark &&
+
+               test_commit important-path &&
+
+               test_commit ignored &&
+
+               git branch A &&
+               git branch B &&
+
+               echo foo >>important-path.t &&
+               git add important-path.t &&
+               test_commit more changes &&
+
+               git fast-export --all -- important-path.t | git fast-import --force
+       )
+'
+
 test_expect_success 'full-tree re-shows unmodified files'        '
        git checkout -f simple &&
        git fast-export --full-tree simple >actual &&
@@ -508,10 +572,20 @@ test_expect_success 'use refspec' '
        test_cmp expected actual
 '
 
-test_expect_success 'delete refspec' '
+test_expect_success 'delete ref because entire history excluded' '
        git branch to-delete &&
-       git fast-export --refspec :refs/heads/to-delete to-delete ^to-delete > actual &&
-       cat > expected <<-EOF &&
+       git fast-export to-delete ^to-delete >actual &&
+       cat >expected <<-EOF &&
+       reset refs/heads/to-delete
+       from 0000000000000000000000000000000000000000
+
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'delete refspec' '
+       git fast-export --refspec :refs/heads/to-delete >actual &&
+       cat >expected <<-EOF &&
        reset refs/heads/to-delete
        from 0000000000000000000000000000000000000000
 
index 2325599ee60f5bf9b179f4a33ccbe866458c0740..850d97911966fe78f22035dab78ac0556a82fd99 100755 (executable)
@@ -500,6 +500,10 @@ test_expect_success 'submit --shelve' '
        )
 '
 
+last_shelve () {
+       p4 -G changes -s shelved -m 1 //depot/... | marshal_dump change
+}
+
 make_shelved_cl() {
        test_commit "$1" >/dev/null &&
        git p4 submit --origin HEAD^ --shelve >/dev/null &&
@@ -533,12 +537,59 @@ test_expect_success 'submit --update-shelve' '
        ) &&
        (
                cd "$cli" &&
-               change=$(p4 -G changes -s shelved -m 1 //depot/... | \
-                        marshal_dump change) &&
+               change=$(last_shelve) &&
                p4 unshelve -c $change -s $change &&
                grep -q updated-line shelf.t &&
                p4 describe -S $change | grep added-file.t &&
-               test_path_is_missing shelved-change-1.t
+               test_path_is_missing shelved-change-1.t &&
+               p4 revert ...
+       )
+'
+
+test_expect_success 'update a shelve involving moved and copied files' '
+       test_when_finished cleanup_git &&
+       (
+               cd "$cli" &&
+               : >file_to_move &&
+               p4 add file_to_move &&
+               p4 submit -d "change1" &&
+               p4 edit file_to_move &&
+               echo change >>file_to_move &&
+               p4 submit -d "change2" &&
+               p4 opened
+       ) &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git config git-p4.detectCopies true &&
+               git config git-p4.detectRenames true &&
+               git config git-p4.skipSubmitEdit true &&
+               mkdir moved &&
+               cp file_to_move copy_of_file &&
+               git add copy_of_file &&
+               git mv file_to_move moved/ &&
+               git commit -m "rename a file" &&
+               git p4 submit -M --shelve --origin HEAD^ &&
+               : >new_file &&
+               git add new_file &&
+               git commit --amend &&
+               git show --stat HEAD &&
+               change=$(last_shelve) &&
+               git p4 submit -M --update-shelve $change --commit HEAD
+       ) &&
+       (
+               cd "$cli" &&
+               change=$(last_shelve) &&
+               echo change=$change &&
+               p4 unshelve -s $change &&
+               p4 submit -d "Testing update-shelve" &&
+               test_path_is_file copy_of_file &&
+               test_path_is_file moved/file_to_move &&
+               test_path_is_missing file_to_move &&
+               test_path_is_file new_file &&
+               echo "unshelved and submitted change $change" &&
+               p4 changes moved/file_to_move | grep "Testing update-shelve" &&
+               p4 changes copy_of_file | grep "Testing update-shelve"
        )
 '
 
index 137fdc9bd525bea9d6ccde256ee449ed389255c0..3a2c6326d83b760194c600e2ccde619438200508 100755 (executable)
@@ -1434,7 +1434,8 @@ test_expect_success 'double dash "git checkout"' '
        --ignore-other-worktrees Z
        --recurse-submodules Z
        --progress Z
-       --no-quiet Z
+       --guess Z
+       --no-guess Z
        --no-... Z
        EOF
 '
@@ -1515,8 +1516,8 @@ test_expect_success 'show completes all refs' '
 
 test_expect_success '<ref>: completes paths' '
        test_completion "git show mytag:f" <<-\EOF
-       file1 Z
-       file2 Z
+       file1Z
+       file2Z
        EOF
 '
 
@@ -1525,7 +1526,7 @@ test_expect_success 'complete tree filename with spaces' '
        git add "name with spaces" &&
        git commit -m spaces &&
        test_completion "git show HEAD:nam" <<-\EOF
-       name with spaces Z
+       name with spacesZ
        EOF
 '
 
@@ -1534,8 +1535,8 @@ test_expect_success 'complete tree filename with metacharacters' '
        git add "name with \${meta}" &&
        git commit -m meta &&
        test_completion "git show HEAD:nam" <<-\EOF
-       name with ${meta} Z
-       name with spaces Z
+       name with ${meta}Z
+       name with spacesZ
        EOF
 '
 
index 81a5179e28bf529bc2f0a8bc197108fcb69fba72..5cadedb2a9bc6eb66b9ead3ccae037e62a655f2c 100755 (executable)
@@ -180,7 +180,7 @@ test_expect_success 'prompt - interactive rebase' '
 '
 
 test_expect_success 'prompt - rebase merge' '
-       printf " (b2|REBASE-m 1/3)" >expected &&
+       printf " (b2|REBASE-i 1/3)" >expected &&
        git checkout b2 &&
        test_when_finished "git checkout master" &&
        test_must_fail git rebase --merge b1 b2 &&
index 6b3bbf99e46cf7cc418eca1eaf768d526ac658aa..92cf8f812cca26c94c8054ccd40af806f6b77310 100644 (file)
@@ -1263,3 +1263,42 @@ test_oid () {
        fi &&
        eval "printf '%s' \"\${$var}\""
 }
+
+# Choose a port number based on the test script's number and store it in
+# the given variable name, unless that variable already contains a number.
+test_set_port () {
+       local var=$1 port
+
+       if test $# -ne 1 || test -z "$var"
+       then
+               BUG "test_set_port requires a variable name"
+       fi
+
+       eval port=\$$var
+       case "$port" in
+       "")
+               # No port is set in the given env var, use the test
+               # number as port number instead.
+               # Remove not only the leading 't', but all leading zeros
+               # as well, so the arithmetic below won't (mis)interpret
+               # a test number like '0123' as an octal value.
+               port=${this_test#${this_test%%[1-9]*}}
+               if test "${port:-0}" -lt 1024
+               then
+                       # root-only port, use a larger one instead.
+                       port=$(($port + 10000))
+               fi
+               ;;
+       *[^0-9]*|0*)
+               error >&7 "invalid port number: $port"
+               ;;
+       *)
+               # The user has specified the port.
+               ;;
+       esac
+
+       # Make sure that parallel '--stress' test jobs get different
+       # ports.
+       port=$(($port + ${GIT_TEST_STRESS_JOB_NR:-0}))
+       eval $var=$port
+}
index 0f1faa24b27b90f4d246d33da9950325f8736633..9876b4bab0044c73bca5eaccdfa5f6611f3f59b7 100644 (file)
@@ -71,19 +71,222 @@ then
        exit 1
 fi
 
+# Parse options while taking care to leave $@ intact, so we will still
+# have all the original command line options when executing the test
+# script again for '--tee' and '--verbose-log' below.
+store_arg_to=
+prev_opt=
+for opt
+do
+       if test -n "$store_arg_to"
+       then
+               eval $store_arg_to=\$opt
+               store_arg_to=
+               prev_opt=
+               continue
+       fi
+
+       case "$opt" in
+       -d|--d|--de|--deb|--debu|--debug)
+               debug=t ;;
+       -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
+               immediate=t ;;
+       -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
+               GIT_TEST_LONG=t; export GIT_TEST_LONG ;;
+       -r)
+               store_arg_to=run_list
+               ;;
+       --run=*)
+               run_list=${opt#--*=} ;;
+       -h|--h|--he|--hel|--help)
+               help=t ;;
+       -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
+               verbose=t ;;
+       --verbose-only=*)
+               verbose_only=${opt#--*=}
+               ;;
+       -q|--q|--qu|--qui|--quie|--quiet)
+               # Ignore --quiet under a TAP::Harness. Saying how many tests
+               # passed without the ok/not ok details is always an error.
+               test -z "$HARNESS_ACTIVE" && quiet=t ;;
+       --with-dashes)
+               with_dashes=t ;;
+       --no-color)
+               color= ;;
+       --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
+               valgrind=memcheck
+               tee=t
+               ;;
+       --valgrind=*)
+               valgrind=${opt#--*=}
+               tee=t
+               ;;
+       --valgrind-only=*)
+               valgrind_only=${opt#--*=}
+               tee=t
+               ;;
+       --tee)
+               tee=t ;;
+       --root=*)
+               root=${opt#--*=} ;;
+       --chain-lint)
+               GIT_TEST_CHAIN_LINT=1 ;;
+       --no-chain-lint)
+               GIT_TEST_CHAIN_LINT=0 ;;
+       -x)
+               trace=t ;;
+       -V|--verbose-log)
+               verbose_log=t
+               tee=t
+               ;;
+       --stress)
+               stress=t ;;
+       --stress=*)
+               stress=${opt#--*=}
+               case "$stress" in
+               *[^0-9]*|0*|"")
+                       echo "error: --stress=<N> requires the number of jobs to run" >&2
+                       exit 1
+                       ;;
+               *)      # Good.
+                       ;;
+               esac
+               ;;
+       *)
+               echo "error: unknown test option '$opt'" >&2; exit 1 ;;
+       esac
+
+       prev_opt=$opt
+done
+if test -n "$store_arg_to"
+then
+       echo "error: $prev_opt requires an argument" >&2
+       exit 1
+fi
+
+if test -n "$valgrind_only"
+then
+       test -z "$valgrind" && valgrind=memcheck
+       test -z "$verbose" && verbose_only="$valgrind_only"
+elif test -n "$valgrind"
+then
+       test -z "$verbose_log" && verbose=t
+fi
+
+if test -n "$stress"
+then
+       verbose=t
+       trace=t
+       immediate=t
+fi
+
+TEST_STRESS_JOB_SFX="${GIT_TEST_STRESS_JOB_NR:+.stress-$GIT_TEST_STRESS_JOB_NR}"
+TEST_NAME="$(basename "$0" .sh)"
+TEST_RESULTS_DIR="$TEST_OUTPUT_DIRECTORY/test-results"
+TEST_RESULTS_BASE="$TEST_RESULTS_DIR/$TEST_NAME$TEST_STRESS_JOB_SFX"
+TRASH_DIRECTORY="trash directory.$TEST_NAME$TEST_STRESS_JOB_SFX"
+test -n "$root" && TRASH_DIRECTORY="$root/$TRASH_DIRECTORY"
+case "$TRASH_DIRECTORY" in
+/*) ;; # absolute path is good
+ *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;;
+esac
+
+# If --stress was passed, run this test repeatedly in several parallel loops.
+if test "$GIT_TEST_STRESS_STARTED" = "done"
+then
+       : # Don't stress test again.
+elif test -n "$stress"
+then
+       if test "$stress" != t
+       then
+               job_count=$stress
+       elif test -n "$GIT_TEST_STRESS_LOAD"
+       then
+               job_count="$GIT_TEST_STRESS_LOAD"
+       elif job_count=$(getconf _NPROCESSORS_ONLN 2>/dev/null) &&
+            test -n "$job_count"
+       then
+               job_count=$((2 * $job_count))
+       else
+               job_count=8
+       fi
+
+       mkdir -p "$TEST_RESULTS_DIR"
+       stressfail="$TEST_RESULTS_BASE.stress-failed"
+       rm -f "$stressfail"
+
+       stress_exit=0
+       trap '
+               kill $job_pids 2>/dev/null
+               wait
+               stress_exit=1
+       ' TERM INT HUP
+
+       job_pids=
+       job_nr=0
+       while test $job_nr -lt "$job_count"
+       do
+               (
+                       GIT_TEST_STRESS_STARTED=done
+                       GIT_TEST_STRESS_JOB_NR=$job_nr
+                       export GIT_TEST_STRESS_STARTED GIT_TEST_STRESS_JOB_NR
+
+                       trap '
+                               kill $test_pid 2>/dev/null
+                               wait
+                               exit 1
+                       ' TERM INT
+
+                       cnt=0
+                       while ! test -e "$stressfail"
+                       do
+                               $TEST_SHELL_PATH "$0" "$@" >"$TEST_RESULTS_BASE.stress-$job_nr.out" 2>&1 &
+                               test_pid=$!
+
+                               if wait $test_pid
+                               then
+                                       printf "OK   %2d.%d\n" $GIT_TEST_STRESS_JOB_NR $cnt
+                               else
+                                       echo $GIT_TEST_STRESS_JOB_NR >>"$stressfail"
+                                       printf "FAIL %2d.%d\n" $GIT_TEST_STRESS_JOB_NR $cnt
+                               fi
+                               cnt=$(($cnt + 1))
+                       done
+               ) &
+               job_pids="$job_pids $!"
+               job_nr=$(($job_nr + 1))
+       done
+
+       wait
+
+       if test -f "$stressfail"
+       then
+               echo "Log(s) of failed test run(s):"
+               for failed_job_nr in $(sort -n "$stressfail")
+               do
+                       echo "Contents of '$TEST_RESULTS_BASE.stress-$failed_job_nr.out':"
+                       cat "$TEST_RESULTS_BASE.stress-$failed_job_nr.out"
+               done
+               rm -rf "$TRASH_DIRECTORY.stress-failed"
+               # Move the last one.
+               mv "$TRASH_DIRECTORY.stress-$failed_job_nr" "$TRASH_DIRECTORY.stress-failed"
+       fi
+
+       exit $stress_exit
+fi
+
 # if --tee was passed, write the output not only to the terminal, but
 # additionally to the file test-results/$BASENAME.out, too.
-case "$GIT_TEST_TEE_STARTED, $* " in
-done,*)
-       # do not redirect again
-       ;;
-*' --tee '*|*' --va'*|*' -V '*|*' --verbose-log '*)
-       mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results"
-       BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)"
+if test "$GIT_TEST_TEE_STARTED" = "done"
+then
+       : # do not redirect again
+elif test -n "$tee"
+then
+       mkdir -p "$TEST_RESULTS_DIR"
 
        # Make this filename available to the sub-process in case it is using
        # --verbose-log.
-       GIT_TEST_TEE_OUTPUT_FILE=$BASE.out
+       GIT_TEST_TEE_OUTPUT_FILE=$TEST_RESULTS_BASE.out
        export GIT_TEST_TEE_OUTPUT_FILE
 
        # Truncate before calling "tee -a" to get rid of the results
@@ -91,11 +294,38 @@ done,*)
        >"$GIT_TEST_TEE_OUTPUT_FILE"
 
        (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
-        echo $? >"$BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
-       test "$(cat "$BASE.exit")" = 0
+        echo $? >"$TEST_RESULTS_BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
+       test "$(cat "$TEST_RESULTS_BASE.exit")" = 0
        exit
-       ;;
-esac
+fi
+
+if test -n "$trace" && test -n "$test_untraceable"
+then
+       # '-x' tracing requested, but this test script can't be reliably
+       # traced, unless it is run with a Bash version supporting
+       # BASH_XTRACEFD (introduced in Bash v4.1).
+       #
+       # Perform this version check _after_ the test script was
+       # potentially re-executed with $TEST_SHELL_PATH for '--tee' or
+       # '--verbose-log', so the right shell is checked and the
+       # warning is issued only once.
+       if test -n "$BASH_VERSION" && eval '
+            test ${BASH_VERSINFO[0]} -gt 4 || {
+              test ${BASH_VERSINFO[0]} -eq 4 &&
+              test ${BASH_VERSINFO[1]} -ge 1
+            }
+          '
+       then
+               : Executed by a Bash version supporting BASH_XTRACEFD.  Good.
+       else
+               echo >&2 "warning: ignoring -x; '$0' is untraceable without BASH_XTRACEFD"
+               trace=
+       fi
+fi
+if test -n "$trace" && test -z "$verbose_log"
+then
+       verbose=t
+fi
 
 # For repeatability, reset the environment to known value.
 # TERM is sanitized below, after saving color control sequences.
@@ -193,7 +423,7 @@ fi
 
 # Add libc MALLOC and MALLOC_PERTURB test
 # only if we are not executing the test with valgrind
-if expr " $GIT_TEST_OPTS " : ".* --valgrind " >/dev/null ||
+if test -n "$valgrind" ||
    test -n "$TEST_NO_MALLOC_CHECK"
 then
        setup_malloc_check () {
@@ -264,100 +494,6 @@ test "x$TERM" != "xdumb" && (
        ) &&
        color=t
 
-while test "$#" -ne 0
-do
-       case "$1" in
-       -d|--d|--de|--deb|--debu|--debug)
-               debug=t; shift ;;
-       -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
-               immediate=t; shift ;;
-       -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
-               GIT_TEST_LONG=t; export GIT_TEST_LONG; shift ;;
-       -r)
-               shift; test "$#" -ne 0 || {
-                       echo 'error: -r requires an argument' >&2;
-                       exit 1;
-               }
-               run_list=$1; shift ;;
-       --run=*)
-               run_list=${1#--*=}; shift ;;
-       -h|--h|--he|--hel|--help)
-               help=t; shift ;;
-       -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
-               verbose=t; shift ;;
-       --verbose-only=*)
-               verbose_only=${1#--*=}
-               shift ;;
-       -q|--q|--qu|--qui|--quie|--quiet)
-               # Ignore --quiet under a TAP::Harness. Saying how many tests
-               # passed without the ok/not ok details is always an error.
-               test -z "$HARNESS_ACTIVE" && quiet=t; shift ;;
-       --with-dashes)
-               with_dashes=t; shift ;;
-       --no-color)
-               color=; shift ;;
-       --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
-               valgrind=memcheck
-               shift ;;
-       --valgrind=*)
-               valgrind=${1#--*=}
-               shift ;;
-       --valgrind-only=*)
-               valgrind_only=${1#--*=}
-               shift ;;
-       --tee)
-               shift ;; # was handled already
-       --root=*)
-               root=${1#--*=}
-               shift ;;
-       --chain-lint)
-               GIT_TEST_CHAIN_LINT=1
-               shift ;;
-       --no-chain-lint)
-               GIT_TEST_CHAIN_LINT=0
-               shift ;;
-       -x)
-               # Some test scripts can't be reliably traced  with '-x',
-               # unless the test is run with a Bash version supporting
-               # BASH_XTRACEFD (introduced in Bash v4.1).  Check whether
-               # this test is marked as such, and ignore '-x' if it
-               # isn't executed with a suitable Bash version.
-               if test -z "$test_untraceable" || {
-                    test -n "$BASH_VERSION" && {
-                      test ${BASH_VERSINFO[0]} -gt 4 || {
-                        test ${BASH_VERSINFO[0]} -eq 4 &&
-                        test ${BASH_VERSINFO[1]} -ge 1
-                      }
-                    }
-                  }
-               then
-                       trace=t
-               else
-                       echo >&2 "warning: ignoring -x; '$0' is untraceable without BASH_XTRACEFD"
-               fi
-               shift ;;
-       -V|--verbose-log)
-               verbose_log=t
-               shift ;;
-       *)
-               echo "error: unknown test option '$1'" >&2; exit 1 ;;
-       esac
-done
-
-if test -n "$valgrind_only"
-then
-       test -z "$valgrind" && valgrind=memcheck
-       test -z "$verbose" && verbose_only="$valgrind_only"
-elif test -n "$valgrind"
-then
-       test -z "$verbose_log" && verbose=t
-fi
-
-if test -n "$trace" && test -z "$verbose_log"
-then
-       verbose=t
-fi
-
 if test -n "$color"
 then
        # Save the color control sequences now rather than run tput
@@ -476,7 +612,7 @@ die () {
 
 GIT_EXIT_OK=
 trap 'die' EXIT
-trap 'exit $?' INT
+trap 'exit $?' INT TERM HUP
 
 # The user-facing functions are loaded from a separate file so that
 # test_perf subshells can have them too
@@ -818,12 +954,9 @@ test_done () {
 
        if test -z "$HARNESS_ACTIVE"
        then
-               test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
-               mkdir -p "$test_results_dir"
-               base=${0##*/}
-               test_results_path="$test_results_dir/${base%.sh}.counts"
+               mkdir -p "$TEST_RESULTS_DIR"
 
-               cat >"$test_results_path" <<-EOF
+               cat >"$TEST_RESULTS_BASE.counts" <<-EOF
                total $test_count
                success $test_success
                fixed $test_fixed
@@ -1021,7 +1154,7 @@ test -d "$GIT_BUILD_DIR"/templates/blt || {
        error "You haven't built things yet, have you?"
 }
 
-if ! test -x "$GIT_BUILD_DIR"/t/helper/test-tool
+if ! test -x "$GIT_BUILD_DIR"/t/helper/test-tool$X
 then
        echo >&2 'You need to build test-tool:'
        echo >&2 'Run "make t/helper/test-tool" in the source (toplevel) directory'
@@ -1029,12 +1162,6 @@ then
 fi
 
 # Test repository
-TRASH_DIRECTORY="trash directory.$(basename "$0" .sh)"
-test -n "$root" && TRASH_DIRECTORY="$root/$TRASH_DIRECTORY"
-case "$TRASH_DIRECTORY" in
-/*) ;; # absolute path is good
- *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;;
-esac
 rm -fr "$TRASH_DIRECTORY" || {
        GIT_EXIT_OK=t
        echo >&5 "FATAL: Cannot prepare test area"
index bf225c698fac81a9a94eff6d3371988ac4ff0bac..1f52c95fd87b1a02968727bde71dcc1eddc50410 100644 (file)
@@ -679,10 +679,15 @@ static int fetch(struct transport *transport,
        if (data->transport_options.update_shallow)
                set_helper_option(transport, "update-shallow", "true");
 
-       if (data->transport_options.filter_options.choice)
-               set_helper_option(
-                       transport, "filter",
-                       data->transport_options.filter_options.filter_spec);
+       if (data->transport_options.filter_options.choice) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
+               expand_list_objects_filter_spec(
+                       &data->transport_options.filter_options,
+                       &expanded_filter_spec);
+               set_helper_option(transport, "filter",
+                                 expanded_filter_spec.buf);
+               strbuf_release(&expanded_filter_spec);
+       }
 
        if (data->transport_options.negotiation_tips)
                warning("Ignoring --negotiation-tip because the protocol does not support it.");
@@ -1026,7 +1031,8 @@ static int push_refs(struct transport *transport,
 }
 
 
-static int has_attribute(const char *attrs, const char *attr) {
+static int has_attribute(const char *attrs, const char *attr)
+{
        int len;
        if (!attrs)
                return 0;
@@ -1225,9 +1231,8 @@ static int udt_do_read(struct unidirectional_transfer *t)
                return 0;       /* No space for more. */
 
        transfer_debug("%s is readable", t->src_name);
-       bytes = read(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse);
-       if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
-               errno != EINTR) {
+       bytes = xread(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse);
+       if (bytes < 0) {
                error_errno(_("read(%s) failed"), t->src_name);
                return -1;
        } else if (bytes == 0) {
@@ -1254,7 +1259,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) {
+       if (bytes < 0) {
                error_errno(_("write(%s) failed"), t->dest_name);
                return -1;
        } else if (bytes > 0) {
index 5a74b609ffad51752ec91cc93c602f00292288d1..e078812897eddefcd5a55a7d474dd4a7a89434fc 100644 (file)
@@ -154,7 +154,7 @@ static int fetch_refs_from_bundle(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
 {
        struct bundle_transport_data *data = transport->data;
-       return unbundle(&data->header, data->fd,
+       return unbundle(the_repository, &data->header, data->fd,
                        transport->progress ? BUNDLE_VERBOSE : 0);
 }
 
@@ -273,7 +273,8 @@ static struct ref *handshake(struct transport *transport, int for_push,
 
        packet_reader_init(&reader, data->fd[0], NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE |
-                          PACKET_READ_GENTLE_ON_EOF);
+                          PACKET_READ_GENTLE_ON_EOF |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
 
        data->version = discover_version(&reader);
        switch (data->version) {
@@ -1105,7 +1106,8 @@ static int run_pre_push_hook(struct transport *transport,
        return ret;
 }
 
-int transport_push(struct transport *transport,
+int transport_push(struct repository *r,
+                  struct transport *transport,
                   struct refspec *rs, int flags,
                   unsigned int *reject_reasons)
 {
@@ -1172,7 +1174,7 @@ int transport_push(struct transport *transport,
                                        oid_array_append(&commits,
                                                          &ref->new_oid);
 
-                       if (!push_unpushed_submodules(the_repository,
+                       if (!push_unpushed_submodules(r,
                                                      &commits,
                                                      transport->remote,
                                                      rs,
@@ -1197,7 +1199,7 @@ int transport_push(struct transport *transport,
                                        oid_array_append(&commits,
                                                          &ref->new_oid);
 
-                       if (find_unpushed_submodules(the_repository,
+                       if (find_unpushed_submodules(r,
                                                     &commits,
                                                     transport->remote->name,
                                                     &needs_pushing)) {
@@ -1433,7 +1435,7 @@ struct alternate_refs_data {
        void *data;
 };
 
-static int refs_from_alternate_cb(struct alternate_object_database *e,
+static int refs_from_alternate_cb(struct object_directory *e,
                                  void *data)
 {
        struct strbuf path = STRBUF_INIT;
index 9baeca2d7a34cf064a632a7677429f477cb8e014..f2ee7c4f499a0937ba4c7148db045665fcb7e4cf 100644 (file)
@@ -223,7 +223,8 @@ void transport_set_verbosity(struct transport *transport, int verbosity,
 #define REJECT_FETCH_FIRST     0x08
 #define REJECT_NEEDS_FORCE     0x10
 
-int transport_push(struct transport *connection,
+int transport_push(struct repository *repo,
+                  struct transport *connection,
                   struct refspec *rs, int flags,
                   unsigned int * reject_reasons);
 
index 0e5432461026eff15fe101cf017cd839702e5870..e6d306f69f940edd9613de0b38ffe2d9993f91ab 100644 (file)
@@ -239,7 +239,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
                                                DIFF_STATUS_ADDED;
 
                        if (tpi_valid) {
-                               oid_i = tp[i].entry.oid;
+                               oid_i = &tp[i].entry.oid;
                                mode_i = tp[i].entry.mode;
                        }
                        else {
@@ -280,7 +280,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_oid[i] = tpi_valid ? tp[i].entry.oid : NULL;
+                       parents_oid[i] = tpi_valid ? &tp[i].entry.oid : NULL;
                }
 
                strbuf_add(base, path, pathlen);
@@ -299,7 +299,8 @@ static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
        enum interesting match;
 
        while (t->size) {
-               match = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec);
+               match = tree_entry_interesting(opt->repo->index, &t->entry,
+                                              base, 0, &opt->pathspec);
                if (match) {
                        if (match == all_entries_not_interesting)
                                t->size = 0;
@@ -491,7 +492,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
                                                continue;
 
                                        /* diff(t,pi) != Ã¸ */
-                                       if (!oideq(t.entry.oid, tp[i].entry.oid) ||
+                                       if (!oideq(&t.entry.oid, &tp[i].entry.oid) ||
                                            (t.entry.mode != tp[i].entry.mode))
                                                continue;
 
index 79bafbd1a23c4a9e20ec623c084778904c534be7..1e4bbc8a0e48c6afe61dc8aab76488356d7e1cbc 100644 (file)
@@ -48,7 +48,8 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
        /* Initialize the descriptor entry */
        desc->entry.path = path;
        desc->entry.mode = canon_mode(mode);
-       desc->entry.oid  = (const struct object_id *)(path + len);
+       desc->entry.pathlen = len - 1;
+       hashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);
 
        return 0;
 }
@@ -107,7 +108,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a)
 static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
 {
        const void *buf = desc->buffer;
-       const unsigned char *end = desc->entry.oid->hash + the_hash_algo->rawsz;
+       const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz;
        unsigned long size = desc->size;
        unsigned long len = end - (const unsigned char *)buf;
 
@@ -175,9 +176,11 @@ void setup_traverse_info(struct traverse_info *info, const char *base)
                pathlen--;
        info->pathlen = pathlen ? pathlen + 1 : 0;
        info->name.path = base;
-       info->name.oid = (void *)(base + pathlen + 1);
-       if (pathlen)
+       info->name.pathlen = pathlen;
+       if (pathlen) {
+               hashcpy(info->name.oid.hash, (const unsigned char *)base + pathlen + 1);
                info->prev = &dummy;
+       }
 }
 
 char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
@@ -365,7 +368,8 @@ static void free_extended_entry(struct tree_desc_x *t)
        }
 }
 
-static inline int prune_traversal(struct name_entry *e,
+static inline int prune_traversal(struct index_state *istate,
+                                 struct name_entry *e,
                                  struct traverse_info *info,
                                  struct strbuf *base,
                                  int still_interesting)
@@ -374,10 +378,13 @@ static inline int prune_traversal(struct name_entry *e,
                return 2;
        if (still_interesting < 0)
                return still_interesting;
-       return tree_entry_interesting(e, base, 0, info->pathspec);
+       return tree_entry_interesting(istate, e, base,
+                                     0, info->pathspec);
 }
 
-int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
+int traverse_trees(struct index_state *istate,
+                  int n, struct tree_desc *t,
+                  struct traverse_info *info)
 {
        int error = 0;
        struct name_entry *entry = xmalloc(n*sizeof(*entry));
@@ -461,7 +468,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
                }
                if (!mask)
                        break;
-               interesting = prune_traversal(e, info, &base, interesting);
+               interesting = prune_traversal(istate, e, info, &base, interesting);
                if (interesting < 0)
                        break;
                if (interesting) {
@@ -498,10 +505,10 @@ static int find_tree_entry(struct tree_desc *t, const char *name, struct object_
        int namelen = strlen(name);
        while (t->size) {
                const char *entry;
-               const struct object_id *oid;
+               struct object_id oid;
                int entrylen, cmp;
 
-               oid = tree_entry_extract(t, &entry, mode);
+               oidcpy(&oid, tree_entry_extract(t, &entry, mode));
                entrylen = tree_entry_len(&t->entry);
                update_tree_entry(t);
                if (entrylen > namelen)
@@ -512,7 +519,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, struct object_
                if (cmp < 0)
                        break;
                if (entrylen == namelen) {
-                       oidcpy(result, oid);
+                       oidcpy(result, &oid);
                        return 0;
                }
                if (name[entrylen] != '/')
@@ -520,10 +527,10 @@ static int find_tree_entry(struct tree_desc *t, const char *name, struct object_
                if (!S_ISDIR(*mode))
                        break;
                if (++entrylen == namelen) {
-                       oidcpy(result, oid);
+                       oidcpy(result, &oid);
                        return 0;
                }
-               return get_tree_entry(oid, name + entrylen, result, mode);
+               return get_tree_entry(&oid, name + entrylen, result, mode);
        }
        return -1;
 }
@@ -575,10 +582,10 @@ int get_tree_entry(const struct object_id *tree_oid, const char *name, struct ob
  * with the sha1 of the found object, and *mode will hold the mode of
  * the object.
  *
- * See the code for enum follow_symlink_result for a description of
+ * See the code for enum get_oid_result for a description of
  * the return values.
  */
-enum follow_symlinks_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode)
+enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode)
 {
        int retval = MISSING_OBJECT;
        struct dir_state *parents = NULL;
@@ -928,7 +935,8 @@ static int match_wildcard_base(const struct pathspec_item *item,
  * Pre-condition: either baselen == base_offset (i.e. empty path)
  * or base[baselen-1] == '/' (i.e. with trailing slash).
  */
-static enum interesting do_match(const struct name_entry *entry,
+static enum interesting do_match(struct index_state *istate,
+                                const struct name_entry *entry,
                                 struct strbuf *base, int base_offset,
                                 const struct pathspec *ps,
                                 int exclude)
@@ -944,7 +952,8 @@ static enum interesting do_match(const struct name_entry *entry,
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        if (!ps->nr) {
                if (!ps->recursive ||
@@ -976,14 +985,20 @@ static enum interesting do_match(const struct name_entry *entry,
 
                        if (!ps->recursive ||
                            !(ps->magic & PATHSPEC_MAXDEPTH) ||
-                           ps->max_depth == -1)
-                               return all_entries_interesting;
-
-                       return within_depth(base_str + matchlen + 1,
-                                           baselen - matchlen - 1,
-                                           !!S_ISDIR(entry->mode),
-                                           ps->max_depth) ?
-                               entry_interesting : entry_not_interesting;
+                           ps->max_depth == -1) {
+                               if (!item->attr_match_nr)
+                                       return all_entries_interesting;
+                               else
+                                       goto interesting;
+                       }
+
+                       if (within_depth(base_str + matchlen + 1,
+                                        baselen - matchlen - 1,
+                                        !!S_ISDIR(entry->mode),
+                                        ps->max_depth))
+                               goto interesting;
+                       else
+                               return entry_not_interesting;
                }
 
                /* Either there must be no base, or the base must match. */
@@ -991,12 +1006,12 @@ static enum interesting do_match(const struct name_entry *entry,
                        if (match_entry(item, entry, pathlen,
                                        match + baselen, matchlen - baselen,
                                        &never_interesting))
-                               return entry_interesting;
+                               goto interesting;
 
                        if (item->nowildcard_len < item->len) {
                                if (!git_fnmatch(item, match + baselen, entry->path,
                                                 item->nowildcard_len - baselen))
-                                       return entry_interesting;
+                                       goto interesting;
 
                                /*
                                 * Match all directories. We'll try to
@@ -1017,7 +1032,7 @@ static enum interesting do_match(const struct name_entry *entry,
                                    !ps_strncmp(item, match + baselen,
                                                entry->path,
                                                item->nowildcard_len - baselen))
-                                       return entry_interesting;
+                                       goto interesting;
                        }
 
                        continue;
@@ -1052,7 +1067,7 @@ match_wildcards:
                if (!git_fnmatch(item, match, base->buf + base_offset,
                                 item->nowildcard_len)) {
                        strbuf_setlen(base, base_offset + baselen);
-                       return entry_interesting;
+                       goto interesting;
                }
 
                /*
@@ -1066,7 +1081,7 @@ match_wildcards:
                    !ps_strncmp(item, match, base->buf + base_offset,
                                item->nowildcard_len)) {
                        strbuf_setlen(base, base_offset + baselen);
-                       return entry_interesting;
+                       goto interesting;
                }
 
                strbuf_setlen(base, base_offset + baselen);
@@ -1080,6 +1095,38 @@ match_wildcards:
                 */
                if (ps->recursive && S_ISDIR(entry->mode))
                        return entry_interesting;
+               continue;
+interesting:
+               if (item->attr_match_nr) {
+                       int ret;
+
+                       /*
+                        * Must not return all_entries_not_interesting
+                        * prematurely. We do not know if all entries do not
+                        * match some attributes with current attr API.
+                        */
+                       never_interesting = entry_not_interesting;
+
+                       /*
+                        * Consider all directories interesting (because some
+                        * of those files inside may match some attributes
+                        * even though the parent dir does not)
+                        *
+                        * FIXME: attributes _can_ match directories and we
+                        * can probably return all_entries_interesting or
+                        * all_entries_not_interesting here if matched.
+                        */
+                       if (S_ISDIR(entry->mode))
+                               return entry_interesting;
+
+                       strbuf_add(base, entry->path, pathlen);
+                       ret = match_pathspec_attrs(istate, base->buf + base_offset,
+                                                  base->len - base_offset, item);
+                       strbuf_setlen(base, base_offset + baselen);
+                       if (!ret)
+                               continue;
+               }
+               return entry_interesting;
        }
        return never_interesting; /* No matches */
 }
@@ -1090,12 +1137,13 @@ match_wildcards:
  * Pre-condition: either baselen == base_offset (i.e. empty path)
  * or base[baselen-1] == '/' (i.e. with trailing slash).
  */
-enum interesting tree_entry_interesting(const struct name_entry *entry,
+enum interesting tree_entry_interesting(struct index_state *istate,
+                                       const struct name_entry *entry,
                                        struct strbuf *base, int base_offset,
                                        const struct pathspec *ps)
 {
        enum interesting positive, negative;
-       positive = do_match(entry, base, base_offset, ps, 0);
+       positive = do_match(istate, entry, base, base_offset, ps, 0);
 
        /*
         * case | entry | positive | negative | result
@@ -1132,7 +1180,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
            positive <= entry_not_interesting) /* #1, #2, #11, #12 */
                return positive;
 
-       negative = do_match(entry, base, base_offset, ps, 1);
+       negative = do_match(istate, entry, base, base_offset, ps, 1);
 
        /* #8, #18 */
        if (positive == all_entries_interesting &&
index 196831007e618f808661bf4b7f54030890f48563..82251718666eb5dceb33d6140e86de5a627baab1 100644 (file)
@@ -1,11 +1,12 @@
 #ifndef TREE_WALK_H
 #define TREE_WALK_H
 
-struct strbuf;
+#include "cache.h"
 
 struct name_entry {
-       const struct object_id *oid;
+       struct object_id oid;
        const char *path;
+       int pathlen;
        unsigned int mode;
 };
 
@@ -19,12 +20,12 @@ static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,
 {
        *pathp = desc->entry.path;
        *modep = desc->entry.mode;
-       return desc->entry.oid;
+       return &desc->entry.oid;
 }
 
 static inline int tree_entry_len(const struct name_entry *ne)
 {
-       return (const char *)ne->oid - ne->path - 1;
+       return ne->pathlen;
 }
 
 /*
@@ -48,25 +49,9 @@ void *fill_tree_descriptor(struct tree_desc *desc, const struct object_id *oid);
 
 struct traverse_info;
 typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
-int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
-
-enum follow_symlinks_result {
-       FOUND = 0, /* This includes out-of-tree links */
-       MISSING_OBJECT = -1, /* The initial symlink is missing */
-       DANGLING_SYMLINK = -2, /*
-                               * The initial symlink is there, but
-                               * (transitively) points to a missing
-                               * in-tree file
-                               */
-       SYMLINK_LOOP = -3,
-       NOT_DIR = -4, /*
-                      * Somewhere along the symlink chain, a path is
-                      * requested which contains a file as a
-                      * non-final element.
-                      */
-};
+int traverse_trees(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info);
 
-enum follow_symlinks_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode);
+enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode);
 
 struct traverse_info {
        const char *traverse_path;
@@ -98,8 +83,9 @@ enum interesting {
        all_entries_interesting = 2 /* yes, and all subsequent entries will be */
 };
 
-extern enum interesting tree_entry_interesting(const struct name_entry *,
-                                              struct strbuf *, int,
-                                              const struct pathspec *ps);
+enum interesting tree_entry_interesting(struct index_state *istate,
+                                       const struct name_entry *,
+                                       struct strbuf *, int,
+                                       const struct pathspec *ps);
 
 #endif
diff --git a/tree.c b/tree.c
index 215d3fdc7c4af2ef2faca1cf5d5d0b5de52b84a8..9f0457c05acf1e4d7bd9f215dd3c710b3cb2df4f 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -60,7 +60,8 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base
                                  ADD_CACHE_JUST_APPEND);
 }
 
-static int read_tree_1(struct tree *tree, struct strbuf *base,
+static int read_tree_1(struct repository *r,
+                      struct tree *tree, struct strbuf *base,
                       int stage, const struct pathspec *pathspec,
                       read_tree_fn_t fn, void *context)
 {
@@ -77,14 +78,15 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
 
        while (tree_entry(&desc, &entry)) {
                if (retval != all_entries_interesting) {
-                       retval = tree_entry_interesting(&entry, base, 0, pathspec);
+                       retval = tree_entry_interesting(r->index, &entry,
+                                                       base, 0, pathspec);
                        if (retval == all_entries_not_interesting)
                                break;
                        if (retval == entry_not_interesting)
                                continue;
                }
 
-               switch (fn(entry.oid, base,
+               switch (fn(&entry.oid, base,
                           entry.path, entry.mode, stage, context)) {
                case 0:
                        continue;
@@ -95,19 +97,19 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
                }
 
                if (S_ISDIR(entry.mode))
-                       oidcpy(&oid, entry.oid);
+                       oidcpy(&oid, &entry.oid);
                else if (S_ISGITLINK(entry.mode)) {
                        struct commit *commit;
 
-                       commit = lookup_commit(the_repository, entry.oid);
+                       commit = lookup_commit(r, &entry.oid);
                        if (!commit)
                                die("Commit %s in submodule path %s%s not found",
-                                   oid_to_hex(entry.oid),
+                                   oid_to_hex(&entry.oid),
                                    base->buf, entry.path);
 
                        if (parse_commit(commit))
                                die("Invalid commit %s in submodule path %s%s",
-                                   oid_to_hex(entry.oid),
+                                   oid_to_hex(&entry.oid),
                                    base->buf, entry.path);
 
                        oidcpy(&oid, get_commit_tree_oid(commit));
@@ -118,7 +120,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
                len = tree_entry_len(&entry);
                strbuf_add(base, entry.path, len);
                strbuf_addch(base, '/');
-               retval = read_tree_1(lookup_tree(the_repository, &oid),
+               retval = read_tree_1(r, lookup_tree(r, &oid),
                                     base, stage, pathspec,
                                     fn, context);
                strbuf_setlen(base, oldlen);
@@ -128,7 +130,8 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
        return 0;
 }
 
-int read_tree_recursive(struct tree *tree,
+int read_tree_recursive(struct repository *r,
+                       struct tree *tree,
                        const char *base, int baselen,
                        int stage, const struct pathspec *pathspec,
                        read_tree_fn_t fn, void *context)
@@ -137,7 +140,7 @@ int read_tree_recursive(struct tree *tree,
        int ret;
 
        strbuf_add(&sb, base, baselen);
-       ret = read_tree_1(tree, &sb, stage, pathspec, fn, context);
+       ret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);
        strbuf_release(&sb);
        return ret;
 }
@@ -152,8 +155,8 @@ static int cmp_cache_name_compare(const void *a_, const void *b_)
                                  ce2->name, ce2->ce_namelen, ce_stage(ce2));
 }
 
-int read_tree(struct tree *tree, int stage, struct pathspec *match,
-             struct index_state *istate)
+int read_tree(struct repository *r, struct tree *tree, int stage,
+             struct pathspec *match, struct index_state *istate)
 {
        read_tree_fn_t fn = NULL;
        int i, err;
@@ -181,7 +184,7 @@ int read_tree(struct tree *tree, int stage, struct pathspec *match,
 
        if (!fn)
                fn = read_one_entry_quick;
-       err = read_tree_recursive(tree, "", 0, stage, match, fn, istate);
+       err = read_tree_recursive(r, tree, "", 0, stage, match, fn, istate);
        if (fn == read_one_entry || err)
                return err;
 
diff --git a/tree.h b/tree.h
index d4807dc805827e6fe07fd01721a8a4680af3403f..93837450739c23e69f3040b8747a54ede26df646 100644 (file)
--- a/tree.h
+++ b/tree.h
@@ -3,7 +3,7 @@
 
 #include "object.h"
 
-extern const char *tree_type;
+struct repository;
 struct strbuf;
 
 struct tree {
@@ -12,6 +12,8 @@ struct tree {
        unsigned long size;
 };
 
+extern const char *tree_type;
+
 struct tree *lookup_tree(struct repository *r, const struct object_id *oid);
 
 int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
@@ -29,12 +31,14 @@ struct tree *parse_tree_indirect(const struct object_id *oid);
 #define READ_TREE_RECURSIVE 1
 typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);
 
-extern int read_tree_recursive(struct tree *tree,
-                              const char *base, int baselen,
-                              int stage, const struct pathspec *pathspec,
-                              read_tree_fn_t fn, void *context);
+int read_tree_recursive(struct repository *r,
+                       struct tree *tree,
+                       const char *base, int baselen,
+                       int stage, const struct pathspec *pathspec,
+                       read_tree_fn_t fn, void *context);
 
-extern int read_tree(struct tree *tree, int stage, struct pathspec *pathspec,
-                    struct index_state *istate);
+int read_tree(struct repository *r, struct tree *tree,
+             int stage, struct pathspec *pathspec,
+             struct index_state *istate);
 
 #endif /* TREE_H */
index 7570df481bf69824e4b163a6c7a15985b72d1326..01c2175f7c12f2bbfe6d24c73d32c60131fdc723 100644 (file)
@@ -294,7 +294,7 @@ static void load_gitmodules_file(struct index_state *index,
                        repo_read_gitmodules(the_repository);
                } else if (state && (ce->ce_flags & CE_UPDATE)) {
                        submodule_free(the_repository);
-                       checkout_entry(ce, state, NULL);
+                       checkout_entry(ce, state, NULL, NULL);
                        repo_read_gitmodules(the_repository);
                }
        }
@@ -450,12 +450,12 @@ static int check_updates(struct unpack_trees_options *o)
                        display_progress(progress, ++cnt);
                        ce->ce_flags &= ~CE_UPDATE;
                        if (o->update && !o->dry_run) {
-                               errs |= checkout_entry(ce, &state, NULL);
+                               errs |= checkout_entry(ce, &state, NULL, NULL);
                        }
                }
        }
        stop_progress(&progress);
-       errs |= finish_delayed_checkout(&state);
+       errs |= finish_delayed_checkout(&state, NULL);
        if (o->update)
                git_attr_set_direction(GIT_ATTR_CHECKIN);
 
@@ -679,7 +679,7 @@ static int switch_cache_bottom(struct traverse_info *info)
 
 static inline int are_same_oid(struct name_entry *name_j, struct name_entry *name_k)
 {
-       return name_j->oid && name_k->oid && oideq(name_j->oid, name_k->oid);
+       return !is_null_oid(&name_j->oid) && !is_null_oid(&name_k->oid) && oideq(&name_j->oid, &name_k->oid);
 }
 
 static int all_trees_same_as_cache_tree(int n, unsigned long dirmask,
@@ -794,6 +794,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
                                    struct name_entry *names,
                                    struct traverse_info *info)
 {
+       struct unpack_trees_options *o = info->data;
        int i, ret, bottom;
        int nr_buf = 0;
        struct tree_desc t[MAX_UNPACK_TREES];
@@ -804,7 +805,6 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
 
        nr_entries = all_trees_same_as_cache_tree(n, dirmask, names, info);
        if (nr_entries > 0) {
-               struct unpack_trees_options *o = info->data;
                int pos = index_pos_by_traverse_info(names, info);
 
                if (!o->merge || df_conflicts)
@@ -857,13 +857,13 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
                else {
                        const struct object_id *oid = NULL;
                        if (dirmask & 1)
-                               oid = names[i].oid;
+                               oid = &names[i].oid;
                        buf[nr_buf++] = fill_tree_descriptor(t + i, oid);
                }
        }
 
        bottom = switch_cache_bottom(&newinfo);
-       ret = traverse_trees(n, t, &newinfo);
+       ret = traverse_trees(o->src_index, n, t, &newinfo);
        restore_cache_bottom(&newinfo, bottom);
 
        for (i = 0; i < nr_buf; i++)
@@ -981,7 +981,7 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,
        ce->ce_mode = create_ce_mode(n->mode);
        ce->ce_flags = create_ce_flags(stage);
        ce->ce_namelen = len;
-       oidcpy(&ce->oid, n->oid);
+       oidcpy(&ce->oid, &n->oid);
        make_traverse_path(ce->name, info, n);
 
        return ce;
@@ -1550,7 +1550,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                }
 
                trace_performance_enter();
-               ret = traverse_trees(len, t, &info);
+               ret = traverse_trees(o->src_index, len, t, &info);
                trace_performance_leave("traverse_trees");
                if (ret < 0)
                        goto return_failed;
@@ -1630,7 +1630,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                move_index_extensions(&o->result, o->src_index);
                if (!ret) {
                        if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
-                               cache_tree_verify(&o->result);
+                               cache_tree_verify(the_repository, &o->result);
                        if (!o->result.cache_tree)
                                o->result.cache_tree = cache_tree();
                        if (!cache_tree_fully_valid(o->result.cache_tree))
index 5e81f1ff24f141fc5357522cb0745c7ff0aeb189..d098ef598289d16956309cb73d4518a92c1206af 100644 (file)
@@ -43,7 +43,6 @@
 
 static timestamp_t oldest_have;
 
-static int deepen_relative;
 static int multi_ack;
 static int no_done;
 static int use_thin_pack, use_ofs_delta, use_include_tag;
@@ -71,6 +70,8 @@ static int allow_filter;
 static int allow_ref_in_want;
 static struct list_objects_filter_options filter_options;
 
+static int allow_sideband_all;
+
 static void reset_timeout(void)
 {
        alarm(timeout);
@@ -140,14 +141,17 @@ static void create_pack_file(const struct object_array *have_obj,
        if (use_include_tag)
                argv_array_push(&pack_objects.args, "--include-tag");
        if (filter_options.filter_spec) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
+               expand_list_objects_filter_spec(&filter_options,
+                                               &expanded_filter_spec);
                if (pack_objects.use_shell) {
                        struct strbuf buf = STRBUF_INIT;
-                       sq_quote_buf(&buf, filter_options.filter_spec);
+                       sq_quote_buf(&buf, expanded_filter_spec.buf);
                        argv_array_pushf(&pack_objects.args, "--filter=%s", buf.buf);
                        strbuf_release(&buf);
                } else {
                        argv_array_pushf(&pack_objects.args, "--filter=%s",
-                                        filter_options.filter_spec);
+                                        expanded_filter_spec.buf);
                }
        }
 
@@ -354,7 +358,8 @@ static int ok_to_give_up(const struct object_array *have_obj,
                                            min_generation);
 }
 
-static int get_common_commits(struct object_array *have_obj,
+static int get_common_commits(struct packet_reader *reader,
+                             struct object_array *have_obj,
                              struct object_array *want_obj)
 {
        struct object_id oid;
@@ -366,12 +371,11 @@ static int get_common_commits(struct object_array *have_obj,
        save_commit_buffer = 0;
 
        for (;;) {
-               char *line = packet_read_line(0, NULL);
                const char *arg;
 
                reset_timeout();
 
-               if (!line) {
+               if (packet_reader_read(reader) != PACKET_READ_NORMAL) {
                        if (multi_ack == 2 && got_common
                            && !got_other && ok_to_give_up(have_obj, want_obj)) {
                                sent_ready = 1;
@@ -390,7 +394,7 @@ static int get_common_commits(struct object_array *have_obj,
                        got_other = 0;
                        continue;
                }
-               if (skip_prefix(line, "have ", &arg)) {
+               if (skip_prefix(reader->line, "have ", &arg)) {
                        switch (got_oid(arg, &oid, have_obj)) {
                        case -1: /* they have what we do not */
                                got_other = 1;
@@ -416,7 +420,7 @@ static int get_common_commits(struct object_array *have_obj,
                        }
                        continue;
                }
-               if (!strcmp(line, "done")) {
+               if (!strcmp(reader->line, "done")) {
                        if (have_obj->nr > 0) {
                                if (multi_ack)
                                        packet_write_fmt(1, "ACK %s\n", last_hex);
@@ -425,7 +429,7 @@ static int get_common_commits(struct object_array *have_obj,
                        packet_write_fmt(1, "NAK\n");
                        return -1;
                }
-               die("git upload-pack: expected SHA1 list, got '%s'", line);
+               die("git upload-pack: expected SHA1 list, got '%s'", reader->line);
        }
 }
 
@@ -613,13 +617,14 @@ error:
        }
 }
 
-static void send_shallow(struct commit_list *result)
+static void send_shallow(struct packet_writer *writer,
+                        struct commit_list *result)
 {
        while (result) {
                struct object *object = &result->item->object;
                if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
-                       packet_write_fmt(1, "shallow %s",
-                                        oid_to_hex(&object->oid));
+                       packet_writer_write(writer, "shallow %s",
+                                           oid_to_hex(&object->oid));
                        register_shallow(the_repository, &object->oid);
                        shallow_nr++;
                }
@@ -627,7 +632,8 @@ static void send_shallow(struct commit_list *result)
        }
 }
 
-static void send_unshallow(const struct object_array *shallows,
+static void send_unshallow(struct packet_writer *writer,
+                          const struct object_array *shallows,
                           struct object_array *want_obj)
 {
        int i;
@@ -636,8 +642,8 @@ static void send_unshallow(const struct object_array *shallows,
                struct object *object = shallows->objects[i].item;
                if (object->flags & NOT_SHALLOW) {
                        struct commit_list *parents;
-                       packet_write_fmt(1, "unshallow %s",
-                                        oid_to_hex(&object->oid));
+                       packet_writer_write(writer, "unshallow %s",
+                                           oid_to_hex(&object->oid));
                        object->flags &= ~CLIENT_SHALLOW;
                        /*
                         * We want to _register_ "object" as shallow, but we
@@ -662,7 +668,9 @@ static void send_unshallow(const struct object_array *shallows,
        }
 }
 
-static void deepen(int depth, int deepen_relative,
+static int check_ref(const char *refname_full, const struct object_id *oid,
+                    int flag, void *cb_data);
+static void deepen(struct packet_writer *writer, int depth, int deepen_relative,
                   struct object_array *shallows, struct object_array *want_obj)
 {
        if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
@@ -676,11 +684,18 @@ static void deepen(int depth, int deepen_relative,
                struct object_array reachable_shallows = OBJECT_ARRAY_INIT;
                struct commit_list *result;
 
+               /*
+                * Checking for reachable shallows requires that our refs be
+                * marked with OUR_REF.
+                */
+               head_ref_namespaced(check_ref, NULL);
+               for_each_namespaced_ref(check_ref, NULL);
+
                get_reachable_list(shallows, &reachable_shallows);
                result = get_shallow_commits(&reachable_shallows,
                                             depth + 1,
                                             SHALLOW, NOT_SHALLOW);
-               send_shallow(result);
+               send_shallow(writer, result);
                free_commit_list(result);
                object_array_clear(&reachable_shallows);
        } else {
@@ -688,14 +703,15 @@ static void deepen(int depth, int deepen_relative,
 
                result = get_shallow_commits(want_obj, depth,
                                             SHALLOW, NOT_SHALLOW);
-               send_shallow(result);
+               send_shallow(writer, result);
                free_commit_list(result);
        }
 
-       send_unshallow(shallows, want_obj);
+       send_unshallow(writer, shallows, want_obj);
 }
 
-static void deepen_by_rev_list(int ac, const char **av,
+static void deepen_by_rev_list(struct packet_writer *writer, int ac,
+                              const char **av,
                               struct object_array *shallows,
                               struct object_array *want_obj)
 {
@@ -703,15 +719,17 @@ static void deepen_by_rev_list(int ac, const char **av,
 
        close_commit_graph(the_repository);
        result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
-       send_shallow(result);
+       send_shallow(writer, result);
        free_commit_list(result);
-       send_unshallow(shallows, want_obj);
+       send_unshallow(writer, shallows, want_obj);
 }
 
 /* Returns 1 if a shallow list is sent or 0 otherwise */
-static int send_shallow_list(int depth, int deepen_rev_list,
+static int send_shallow_list(struct packet_writer *writer,
+                            int depth, int deepen_rev_list,
                             timestamp_t deepen_since,
                             struct string_list *deepen_not,
+                            int deepen_relative,
                             struct object_array *shallows,
                             struct object_array *want_obj)
 {
@@ -720,7 +738,7 @@ static int send_shallow_list(int depth, int deepen_rev_list,
        if (depth > 0 && deepen_rev_list)
                die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together");
        if (depth > 0) {
-               deepen(depth, deepen_relative, shallows, want_obj);
+               deepen(writer, depth, deepen_relative, shallows, want_obj);
                ret = 1;
        } else if (deepen_rev_list) {
                struct argv_array av = ARGV_ARRAY_INIT;
@@ -741,7 +759,7 @@ static int send_shallow_list(int depth, int deepen_rev_list,
                        struct object *o = want_obj->objects[i].item;
                        argv_array_push(&av, oid_to_hex(&o->oid));
                }
-               deepen_by_rev_list(av.argc, av.argv, shallows, want_obj);
+               deepen_by_rev_list(writer, av.argc, av.argv, shallows, want_obj);
                argv_array_clear(&av);
                ret = 1;
        } else {
@@ -826,7 +844,7 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not,
        return 0;
 }
 
-static void receive_needs(struct object_array *want_obj)
+static void receive_needs(struct packet_reader *reader, struct object_array *want_obj)
 {
        struct object_array shallows = OBJECT_ARRAY_INIT;
        struct string_list deepen_not = STRING_LIST_INIT_DUP;
@@ -834,39 +852,41 @@ static void receive_needs(struct object_array *want_obj)
        int has_non_tip = 0;
        timestamp_t deepen_since = 0;
        int deepen_rev_list = 0;
+       int deepen_relative = 0;
+       struct packet_writer writer;
 
        shallow_nr = 0;
+       packet_writer_init(&writer, 1);
        for (;;) {
                struct object *o;
                const char *features;
                struct object_id oid_buf;
-               char *line = packet_read_line(0, NULL);
                const char *arg;
 
                reset_timeout();
-               if (!line)
+               if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                        break;
 
-               if (process_shallow(line, &shallows))
+               if (process_shallow(reader->line, &shallows))
                        continue;
-               if (process_deepen(line, &depth))
+               if (process_deepen(reader->line, &depth))
                        continue;
-               if (process_deepen_since(line, &deepen_since, &deepen_rev_list))
+               if (process_deepen_since(reader->line, &deepen_since, &deepen_rev_list))
                        continue;
-               if (process_deepen_not(line, &deepen_not, &deepen_rev_list))
+               if (process_deepen_not(reader->line, &deepen_not, &deepen_rev_list))
                        continue;
 
-               if (skip_prefix(line, "filter ", &arg)) {
+               if (skip_prefix(reader->line, "filter ", &arg)) {
                        if (!filter_capability_requested)
                                die("git upload-pack: filtering capability not negotiated");
                        parse_list_objects_filter(&filter_options, arg);
                        continue;
                }
 
-               if (!skip_prefix(line, "want ", &arg) ||
+               if (!skip_prefix(reader->line, "want ", &arg) ||
                    parse_oid_hex(arg, &oid_buf, &features))
                        die("git upload-pack: protocol error, "
-                           "expected to get object ID, not '%s'", line);
+                           "expected to get object ID, not '%s'", reader->line);
 
                if (parse_feature_request(features, "deepen-relative"))
                        deepen_relative = 1;
@@ -893,9 +913,9 @@ static void receive_needs(struct object_array *want_obj)
 
                o = parse_object(the_repository, &oid_buf);
                if (!o) {
-                       packet_write_fmt(1,
-                                        "ERR upload-pack: not our ref %s",
-                                        oid_to_hex(&oid_buf));
+                       packet_writer_error(&writer,
+                                           "upload-pack: not our ref %s",
+                                           oid_to_hex(&oid_buf));
                        die("git upload-pack: not our ref %s",
                            oid_to_hex(&oid_buf));
                }
@@ -924,8 +944,9 @@ static void receive_needs(struct object_array *want_obj)
        if (depth == 0 && !deepen_rev_list && shallows.nr == 0)
                return;
 
-       if (send_shallow_list(depth, deepen_rev_list, deepen_since,
-                             &deepen_not, &shallows, want_obj))
+       if (send_shallow_list(&writer, depth, deepen_rev_list, deepen_since,
+                             &deepen_not, deepen_relative, &shallows,
+                             want_obj))
                packet_flush(1);
        object_array_clear(&shallows);
 }
@@ -1041,6 +1062,8 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
                allow_filter = git_config_bool(var, value);
        } else if (!strcmp("uploadpack.allowrefinwant", var)) {
                allow_ref_in_want = git_config_bool(var, value);
+       } else if (!strcmp("uploadpack.allowsidebandall", var)) {
+               allow_sideband_all = git_config_bool(var, value);
        }
 
        if (current_config_scope() != CONFIG_SCOPE_REPO) {
@@ -1055,6 +1078,7 @@ void upload_pack(struct upload_pack_options *options)
 {
        struct string_list symref = STRING_LIST_INIT_DUP;
        struct object_array want_obj = OBJECT_ARRAY_INIT;
+       struct packet_reader reader;
 
        stateless_rpc = options->stateless_rpc;
        timeout = options->timeout;
@@ -1078,10 +1102,14 @@ void upload_pack(struct upload_pack_options *options)
        if (options->advertise_refs)
                return;
 
-       receive_needs(&want_obj);
+       packet_reader_init(&reader, 0, NULL, 0,
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
+
+       receive_needs(&reader, &want_obj);
        if (want_obj.nr) {
                struct object_array have_obj = OBJECT_ARRAY_INIT;
-               get_common_commits(&have_obj, &want_obj);
+               get_common_commits(&reader, &have_obj, &want_obj);
                create_pack_file(&have_obj, &want_obj);
        }
 }
@@ -1098,6 +1126,8 @@ struct upload_pack_data {
        int deepen_rev_list;
        int deepen_relative;
 
+       struct packet_writer writer;
+
        unsigned stateless_rpc : 1;
 
        unsigned use_thin_pack : 1;
@@ -1121,6 +1151,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
        data->haves = haves;
        data->shallows = shallows;
        data->deepen_not = deepen_not;
+       packet_writer_init(&data->writer, 1);
 }
 
 static void upload_pack_data_clear(struct upload_pack_data *data)
@@ -1132,7 +1163,8 @@ static void upload_pack_data_clear(struct upload_pack_data *data)
        string_list_clear(&data->deepen_not, 0);
 }
 
-static int parse_want(const char *line, struct object_array *want_obj)
+static int parse_want(struct packet_writer *writer, const char *line,
+                     struct object_array *want_obj)
 {
        const char *arg;
        if (skip_prefix(line, "want ", &arg)) {
@@ -1145,9 +1177,9 @@ static int parse_want(const char *line, struct object_array *want_obj)
 
                o = parse_object(the_repository, &oid);
                if (!o) {
-                       packet_write_fmt(1,
-                                        "ERR upload-pack: not our ref %s",
-                                        oid_to_hex(&oid));
+                       packet_writer_error(writer,
+                                           "upload-pack: not our ref %s",
+                                           oid_to_hex(&oid));
                        die("git upload-pack: not our ref %s",
                            oid_to_hex(&oid));
                }
@@ -1163,7 +1195,8 @@ static int parse_want(const char *line, struct object_array *want_obj)
        return 0;
 }
 
-static int parse_want_ref(const char *line, struct string_list *wanted_refs,
+static int parse_want_ref(struct packet_writer *writer, const char *line,
+                         struct string_list *wanted_refs,
                          struct object_array *want_obj)
 {
        const char *arg;
@@ -1173,7 +1206,7 @@ static int parse_want_ref(const char *line, struct string_list *wanted_refs,
                struct object *o;
 
                if (read_ref(arg, &oid)) {
-                       packet_write_fmt(1, "ERR unknown ref %s", arg);
+                       packet_writer_error(writer, "unknown ref %s", arg);
                        die("unknown ref %s", arg);
                }
 
@@ -1216,10 +1249,11 @@ static void process_args(struct packet_reader *request,
                const char *p;
 
                /* process want */
-               if (parse_want(arg, want_obj))
+               if (parse_want(&data->writer, arg, want_obj))
                        continue;
                if (allow_ref_in_want &&
-                   parse_want_ref(arg, &data->wanted_refs, want_obj))
+                   parse_want_ref(&data->writer, arg, &data->wanted_refs,
+                                  want_obj))
                        continue;
                /* process have line */
                if (parse_have(arg, &data->haves))
@@ -1268,6 +1302,13 @@ static void process_args(struct packet_reader *request,
                        continue;
                }
 
+               if ((git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
+                    allow_sideband_all) &&
+                   !strcmp(arg, "sideband-all")) {
+                       data->writer.use_sideband = 1;
+                       continue;
+               }
+
                /* ignore unknown lines maybe? */
                die("unexpected line: '%s'", arg);
        }
@@ -1313,26 +1354,26 @@ static int process_haves(struct oid_array *haves, struct oid_array *common,
        return 0;
 }
 
-static int send_acks(struct oid_array *acks, struct strbuf *response,
+static int send_acks(struct packet_writer *writer, struct oid_array *acks,
                     const struct object_array *have_obj,
                     struct object_array *want_obj)
 {
        int i;
 
-       packet_buf_write(response, "acknowledgments\n");
+       packet_writer_write(writer, "acknowledgments\n");
 
        /* Send Acks */
        if (!acks->nr)
-               packet_buf_write(response, "NAK\n");
+               packet_writer_write(writer, "NAK\n");
 
        for (i = 0; i < acks->nr; i++) {
-               packet_buf_write(response, "ACK %s\n",
-                                oid_to_hex(&acks->oid[i]));
+               packet_writer_write(writer, "ACK %s\n",
+                                   oid_to_hex(&acks->oid[i]));
        }
 
        if (ok_to_give_up(have_obj, want_obj)) {
                /* Send Ready */
-               packet_buf_write(response, "ready\n");
+               packet_writer_write(writer, "ready\n");
                return 1;
        }
 
@@ -1344,25 +1385,20 @@ static int process_haves_and_send_acks(struct upload_pack_data *data,
                                       struct object_array *want_obj)
 {
        struct oid_array common = OID_ARRAY_INIT;
-       struct strbuf response = STRBUF_INIT;
        int ret = 0;
 
        process_haves(&data->haves, &common, have_obj);
        if (data->done) {
                ret = 1;
-       } else if (send_acks(&common, &response, have_obj, want_obj)) {
-               packet_buf_delim(&response);
+       } else if (send_acks(&data->writer, &common, have_obj, want_obj)) {
+               packet_writer_delim(&data->writer);
                ret = 1;
        } else {
                /* Add Flush */
-               packet_buf_flush(&response);
+               packet_writer_flush(&data->writer);
                ret = 0;
        }
 
-       /* Send response */
-       write_or_die(1, response.buf, response.len);
-       strbuf_release(&response);
-
        oid_array_clear(&data->haves);
        oid_array_clear(&common);
        return ret;
@@ -1375,15 +1411,15 @@ static void send_wanted_ref_info(struct upload_pack_data *data)
        if (!data->wanted_refs.nr)
                return;
 
-       packet_write_fmt(1, "wanted-refs\n");
+       packet_writer_write(&data->writer, "wanted-refs\n");
 
        for_each_string_list_item(item, &data->wanted_refs) {
-               packet_write_fmt(1, "%s %s\n",
-                                oid_to_hex(item->util),
-                                item->string);
+               packet_writer_write(&data->writer, "%s %s\n",
+                                   oid_to_hex(item->util),
+                                   item->string);
        }
 
-       packet_delim(1);
+       packet_writer_delim(&data->writer);
 }
 
 static void send_shallow_info(struct upload_pack_data *data,
@@ -1394,14 +1430,16 @@ static void send_shallow_info(struct upload_pack_data *data,
            !is_repository_shallow(the_repository))
                return;
 
-       packet_write_fmt(1, "shallow-info\n");
+       packet_writer_write(&data->writer, "shallow-info\n");
 
-       if (!send_shallow_list(data->depth, data->deepen_rev_list,
+       if (!send_shallow_list(&data->writer, data->depth,
+                              data->deepen_rev_list,
                               data->deepen_since, &data->deepen_not,
+                              data->deepen_relative,
                               &data->shallows, want_obj) &&
            is_repository_shallow(the_repository))
-               deepen(INFINITE_DEPTH, data->deepen_relative, &data->shallows,
-                      want_obj);
+               deepen(&data->writer, INFINITE_DEPTH, data->deepen_relative,
+                      &data->shallows, want_obj);
 
        packet_delim(1);
 }
@@ -1463,7 +1501,7 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
                        send_wanted_ref_info(&data);
                        send_shallow_info(&data, &want_obj);
 
-                       packet_write_fmt(1, "packfile\n");
+                       packet_writer_write(&data.writer, "packfile\n");
                        create_pack_file(&have_obj, &want_obj);
                        state = FETCH_DONE;
                        break;
@@ -1484,6 +1522,7 @@ int upload_pack_advertise(struct repository *r,
        if (value) {
                int allow_filter_value;
                int allow_ref_in_want;
+               int allow_sideband_all_value;
 
                strbuf_addstr(value, "shallow");
 
@@ -1498,6 +1537,13 @@ int upload_pack_advertise(struct repository *r,
                                         &allow_ref_in_want) &&
                    allow_ref_in_want)
                        strbuf_addstr(value, " ref-in-want");
+
+               if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
+                   (!repo_config_get_bool(the_repository,
+                                          "uploadpack.allowsidebandall",
+                                          &allow_sideband_all_value) &&
+                    allow_sideband_all_value))
+                       strbuf_addstr(value, " sideband-all");
        }
 
        return 1;
diff --git a/url.c b/url.c
index eaf4f07081eae122fbc38e05f60ee2f11a0c65a6..25576c390baa79cb0a203d7f682e8f3442f91a60 100644 (file)
--- a/url.c
+++ b/url.c
@@ -104,7 +104,8 @@ void end_url_with_slash(struct strbuf *buf, const char *url)
        strbuf_complete(buf, '/');
 }
 
-void str_end_url_with_slash(const char *url, char **dest) {
+void str_end_url_with_slash(const char *url, char **dest)
+{
        struct strbuf buf = STRBUF_INIT;
        end_url_with_slash(&buf, url);
        free(*dest);
index 46d34cc2a436c6084531d39628d013785700e2ba..3a78fbf5044fbc5d5624ce94e54eb4ab325e2cf1 100644 (file)
@@ -265,7 +265,8 @@ int userdiff_config(const char *k, const char *v)
        return 0;
 }
 
-struct userdiff_driver *userdiff_find_by_name(const char *name) {
+struct userdiff_driver *userdiff_find_by_name(const char *name)
+{
        int len = strlen(name);
        return userdiff_find_by_namelen(name, len);
 }
@@ -290,7 +291,8 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
        return userdiff_find_by_name(check->items[0].value);
 }
 
-struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver)
+struct userdiff_driver *userdiff_get_textconv(struct repository *r,
+                                             struct userdiff_driver *driver)
 {
        if (!driver->textconv)
                return NULL;
@@ -300,7 +302,7 @@ struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver)
                struct strbuf name = STRBUF_INIT;
 
                strbuf_addf(&name, "textconv/%s", driver->name);
-               notes_cache_init(c, name.buf, driver->textconv);
+               notes_cache_init(r, c, name.buf, driver->textconv);
                driver->textconv_cache = c;
                strbuf_release(&name);
        }
index b072bfe89a46efb47c2808b5469bdf1bbfb4cc63..203057e13e508adaaef1394da9fb086284c79733 100644 (file)
@@ -4,6 +4,7 @@
 #include "notes-cache.h"
 
 struct index_state;
+struct repository;
 
 struct userdiff_funcname {
        const char *pattern;
@@ -30,6 +31,7 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
  * Initialize any textconv-related fields in the driver and return it, or NULL
  * if it does not have textconv enabled at all.
  */
-struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver);
+struct userdiff_driver *userdiff_get_textconv(struct repository *r,
+                                             struct userdiff_driver *driver);
 
 #endif /* USERDIFF */
diff --git a/utf8.c b/utf8.c
index eb785875042920b84e56abbd235ef9eb7ef22ac4..83824dc2f4ab151a19418c61c46e0c1ffbb0e42c 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -4,6 +4,11 @@
 
 /* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
 
+static const char utf16_be_bom[] = {'\xFE', '\xFF'};
+static const char utf16_le_bom[] = {'\xFF', '\xFE'};
+static const char utf32_be_bom[] = {'\0', '\0', '\xFE', '\xFF'};
+static const char utf32_le_bom[] = {'\xFF', '\xFE', '\0', '\0'};
+
 struct interval {
        ucs_char_t first;
        ucs_char_t last;
@@ -470,16 +475,17 @@ int utf8_fprintf(FILE *stream, const char *format, ...)
 #else
        typedef char * iconv_ibp;
 #endif
-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, size_t *outsz_p)
+char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv,
+                           size_t bom_len, size_t *outsz_p)
 {
        size_t outsz, outalloc;
        char *out, *outpos;
        iconv_ibp cp;
 
        outsz = insz;
-       outalloc = st_add(outsz, 1); /* for terminating NUL */
+       outalloc = st_add(outsz, 1 + bom_len); /* for terminating NUL */
        out = xmalloc(outalloc);
-       outpos = out;
+       outpos = out + bom_len;
        cp = (iconv_ibp)in;
 
        while (1) {
@@ -540,10 +546,30 @@ char *reencode_string_len(const char *in, size_t insz,
 {
        iconv_t conv;
        char *out;
+       const char *bom_str = NULL;
+       size_t bom_len = 0;
 
        if (!in_encoding)
                return NULL;
 
+       /* UTF-16LE-BOM is the same as UTF-16 for reading */
+       if (same_utf_encoding("UTF-16LE-BOM", in_encoding))
+               in_encoding = "UTF-16";
+
+       /*
+        * For writing, UTF-16 iconv typically creates "UTF-16BE-BOM"
+        * Some users under Windows want the little endian version
+        */
+       if (same_utf_encoding("UTF-16LE-BOM", out_encoding)) {
+               bom_str = utf16_le_bom;
+               bom_len = sizeof(utf16_le_bom);
+               out_encoding = "UTF-16LE";
+       } else if (same_utf_encoding("UTF-16BE-BOM", out_encoding)) {
+               bom_str = utf16_be_bom;
+               bom_len = sizeof(utf16_be_bom);
+               out_encoding = "UTF-16BE";
+       }
+
        conv = iconv_open(out_encoding, in_encoding);
        if (conv == (iconv_t) -1) {
                in_encoding = fallback_encoding(in_encoding);
@@ -553,9 +579,10 @@ char *reencode_string_len(const char *in, size_t insz,
                if (conv == (iconv_t) -1)
                        return NULL;
        }
-
-       out = reencode_string_iconv(in, insz, conv, outsz);
+       out = reencode_string_iconv(in, insz, conv, bom_len, outsz);
        iconv_close(conv);
+       if (out && bom_str && bom_len)
+               memcpy(out, bom_str, bom_len);
        return out;
 }
 #endif
@@ -566,11 +593,6 @@ static int has_bom_prefix(const char *data, size_t len,
        return data && bom && (len >= bom_len) && !memcmp(data, bom, bom_len);
 }
 
-static const char utf16_be_bom[] = {'\xFE', '\xFF'};
-static const char utf16_le_bom[] = {'\xFF', '\xFE'};
-static const char utf32_be_bom[] = {'\0', '\0', '\xFE', '\xFF'};
-static const char utf32_le_bom[] = {'\xFF', '\xFE', '\0', '\0'};
-
 int has_prohibited_utf_bom(const char *enc, const char *data, size_t len)
 {
        return (
diff --git a/utf8.h b/utf8.h
index edea55e093a4713dcd0056da9a31b63f0cb51272..84efbfcb1fa163b5b799add81b4464ff6eb88611 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -27,7 +27,7 @@ void strbuf_utf8_replace(struct strbuf *sb, int pos, int width,
 
 #ifndef NO_ICONV
 char *reencode_string_iconv(const char *in, size_t insz,
-                           iconv_t conv, size_t *outsz);
+                           iconv_t conv, size_t bom_len, size_t *outsz);
 char *reencode_string_len(const char *in, size_t insz,
                          const char *out_encoding,
                          const char *in_encoding,
index 96990d84dabfdd34e169c8e9fcfd656633e4f31e..d74ae59c77fb3626be0af291c6ee6fbb9c52dbb1 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -50,13 +50,13 @@ static int process_tree(struct walker *walker, struct tree *tree)
                        continue;
                if (S_ISDIR(entry.mode)) {
                        struct tree *tree = lookup_tree(the_repository,
-                                                       entry.oid);
+                                                       &entry.oid);
                        if (tree)
                                obj = &tree->object;
                }
                else {
                        struct blob *blob = lookup_blob(the_repository,
-                                                       entry.oid);
+                                                       &entry.oid);
                        if (blob)
                                obj = &blob->object;
                }
index a24711374c41918bed46fbe5d5a3060312917fa6..0fe3bcd4cd057b72a61cfc6d46e892ac890f0634 100644 (file)
@@ -119,9 +119,10 @@ static void status_printf_more(struct wt_status *s, const char *color,
        va_end(ap);
 }
 
-void wt_status_prepare(struct wt_status *s)
+void wt_status_prepare(struct repository *r, struct wt_status *s)
 {
        memset(s, 0, sizeof(*s));
+       s->repo = r;
        memcpy(s->color_palette, default_wt_status_colors,
               sizeof(default_wt_status_colors));
        s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
@@ -494,19 +495,19 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
        }
 }
 
-static int unmerged_mask(const char *path)
+static int unmerged_mask(struct index_state *istate, const char *path)
 {
        int pos, mask;
        const struct cache_entry *ce;
 
-       pos = cache_name_pos(path, strlen(path));
+       pos = index_name_pos(istate, path, strlen(path));
        if (0 <= pos)
                return 0;
 
        mask = 0;
        pos = -pos-1;
-       while (pos < active_nr) {
-               ce = active_cache[pos++];
+       while (pos < istate->cache_nr) {
+               ce = istate->cache[pos++];
                if (strcmp(ce->name, path) || !ce_stage(ce))
                        break;
                mask |= (1 << (ce_stage(ce) - 1));
@@ -566,7 +567,8 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
                        s->committable = 1;
                        break;
                case DIFF_STATUS_UNMERGED:
-                       d->stagemask = unmerged_mask(p->two->path);
+                       d->stagemask = unmerged_mask(s->repo->index,
+                                                    p->two->path);
                        /*
                         * Don't bother setting {mode,oid}_{head,index} since the print
                         * code will output the stage values directly and not use the
@@ -585,7 +587,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 {
        struct rev_info rev;
 
-       repo_init_revisions(the_repository, &rev, NULL);
+       repo_init_revisions(s->repo, &rev, NULL);
        setup_revisions(0, NULL, &rev, NULL);
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
        rev.diffopt.flags.dirty_submodules = 1;
@@ -610,7 +612,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        struct rev_info rev;
        struct setup_revision_opt opt;
 
-       repo_init_revisions(the_repository, &rev, NULL);
+       repo_init_revisions(s->repo, &rev, NULL);
        memset(&opt, 0, sizeof(opt));
        opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
        setup_revisions(0, NULL, &rev, &opt);
@@ -643,14 +645,15 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 
 static void wt_status_collect_changes_initial(struct wt_status *s)
 {
+       struct index_state *istate = s->repo->index;
        int i;
 
-       for (i = 0; i < active_nr; i++) {
+       for (i = 0; i < istate->cache_nr; i++) {
                struct string_list_item *it;
                struct wt_status_change_data *d;
-               const struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = istate->cache[i];
 
-               if (!ce_path_match(&the_index, ce, &s->pathspec, NULL))
+               if (!ce_path_match(istate, ce, &s->pathspec, NULL))
                        continue;
                if (ce_intent_to_add(ce))
                        continue;
@@ -684,6 +687,7 @@ static void wt_status_collect_untracked(struct wt_status *s)
        int i;
        struct dir_struct dir;
        uint64_t t_begin = getnanotime();
+       struct index_state *istate = s->repo->index;
 
        if (!s->show_untracked_files)
                return;
@@ -698,25 +702,25 @@ static void wt_status_collect_untracked(struct wt_status *s)
                if (s->show_ignored_mode == SHOW_MATCHING_IGNORED)
                        dir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;
        } else {
-               dir.untracked = the_index.untracked;
+               dir.untracked = istate->untracked;
        }
 
        setup_standard_excludes(&dir);
 
-       fill_directory(&dir, &the_index, &s->pathspec);
+       fill_directory(&dir, istate, &s->pathspec);
 
        for (i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
-               if (cache_name_is_other(ent->name, ent->len) &&
-                   dir_path_match(&the_index, ent, &s->pathspec, 0, NULL))
+               if (index_name_is_other(istate, ent->name, ent->len) &&
+                   dir_path_match(istate, ent, &s->pathspec, 0, NULL))
                        string_list_insert(&s->untracked, ent->name);
                free(ent);
        }
 
        for (i = 0; i < dir.ignored_nr; i++) {
                struct dir_entry *ent = dir.ignored[i];
-               if (cache_name_is_other(ent->name, ent->len) &&
-                   dir_path_match(&the_index, ent, &s->pathspec, 0, NULL))
+               if (index_name_is_other(istate, ent->name, ent->len) &&
+                   dir_path_match(istate, ent, &s->pathspec, 0, NULL))
                        string_list_insert(&s->ignored, ent->name);
                free(ent);
        }
@@ -751,7 +755,7 @@ void wt_status_collect(struct wt_status *s)
                wt_status_collect_changes_index(s);
        wt_status_collect_untracked(s);
 
-       wt_status_get_state(&s->state, s->branch && !strcmp(s->branch, "HEAD"));
+       wt_status_get_state(s->repo, &s->state, s->branch && !strcmp(s->branch, "HEAD"));
        if (s->state.merge_in_progress && !has_unmerged(s))
                s->committable = 1;
 }
@@ -1009,7 +1013,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
        int dirty_submodules;
        const char *c = color(WT_STATUS_HEADER, s);
 
-       repo_init_revisions(the_repository, &rev, NULL);
+       repo_init_revisions(s->repo, &rev, NULL);
        rev.diffopt.flags.allow_textconv = 1;
        rev.diffopt.ita_invisible_in_index = 1;
 
@@ -1326,7 +1330,7 @@ static void show_rebase_in_progress(struct wt_status *s,
                                _("  (use \"git rebase --abort\" to check out the original branch)"));
                }
        } else if (s->state.rebase_in_progress ||
-                  !stat(git_path_merge_msg(the_repository), &st)) {
+                  !stat(git_path_merge_msg(s->repo), &st)) {
                print_rebase_state(s, color);
                if (s->hints)
                        status_printf_ln(s, color,
@@ -1478,7 +1482,8 @@ static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
        return 1;
 }
 
-static void wt_status_get_detached_from(struct wt_status_state *state)
+static void wt_status_get_detached_from(struct repository *r,
+                                       struct wt_status_state *state)
 {
        struct grab_1st_switch_cbdata cb;
        struct commit *commit;
@@ -1495,7 +1500,7 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
            /* sha1 is a commit? match without further lookup */
            (oideq(&cb.noid, &oid) ||
             /* perhaps sha1 is a tag, try to dereference to a commit */
-            ((commit = lookup_commit_reference_gently(the_repository, &oid, 1)) != NULL &&
+            ((commit = lookup_commit_reference_gently(r, &oid, 1)) != NULL &&
              oideq(&cb.noid, &commit->object.oid)))) {
                const char *from = ref;
                if (!skip_prefix(from, "refs/tags/", &from))
@@ -1552,31 +1557,32 @@ int wt_status_check_bisect(const struct worktree *wt,
        return 0;
 }
 
-void wt_status_get_state(struct wt_status_state *state,
+void wt_status_get_state(struct repository *r,
+                        struct wt_status_state *state,
                         int get_detached_from)
 {
        struct stat st;
        struct object_id oid;
 
-       if (!stat(git_path_merge_head(the_repository), &st)) {
+       if (!stat(git_path_merge_head(r), &st)) {
                wt_status_check_rebase(NULL, state);
                state->merge_in_progress = 1;
        } else if (wt_status_check_rebase(NULL, state)) {
                ;               /* all set */
-       } else if (!stat(git_path_cherry_pick_head(the_repository), &st) &&
+       } else if (!stat(git_path_cherry_pick_head(r), &st) &&
                        !get_oid("CHERRY_PICK_HEAD", &oid)) {
                state->cherry_pick_in_progress = 1;
                oidcpy(&state->cherry_pick_head_oid, &oid);
        }
        wt_status_check_bisect(NULL, state);
-       if (!stat(git_path_revert_head(the_repository), &st) &&
+       if (!stat(git_path_revert_head(r), &st) &&
            !get_oid("REVERT_HEAD", &oid)) {
                state->revert_in_progress = 1;
                oidcpy(&state->revert_head_oid, &oid);
        }
 
        if (get_detached_from)
-               wt_status_get_detached_from(state);
+               wt_status_get_detached_from(r, state);
 }
 
 static void wt_longstatus_print_state(struct wt_status *s)
@@ -2140,6 +2146,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
        struct wt_status *s)
 {
        struct wt_status_change_data *d = it->util;
+       struct index_state *istate = s->repo->index;
        const struct cache_entry *ce;
        struct strbuf buf_index = STRBUF_INIT;
        const char *path_index = NULL;
@@ -2178,11 +2185,11 @@ static void wt_porcelain_v2_print_unmerged_entry(
         */
        memset(stages, 0, sizeof(stages));
        sum = 0;
-       pos = cache_name_pos(it->string, strlen(it->string));
+       pos = index_name_pos(istate, it->string, strlen(it->string));
        assert(pos < 0);
        pos = -pos-1;
-       while (pos < active_nr) {
-               ce = active_cache[pos++];
+       while (pos < istate->cache_nr) {
+               ce = istate->cache[pos++];
                stage = ce_stage(ce);
                if (strcmp(ce->name, it->string) || !stage)
                        break;
@@ -2307,12 +2314,12 @@ void wt_status_print(struct wt_status *s)
 /**
  * Returns 1 if there are unstaged changes, 0 otherwise.
  */
-int has_unstaged_changes(int ignore_submodules)
+int has_unstaged_changes(struct repository *r, int ignore_submodules)
 {
        struct rev_info rev_info;
        int result;
 
-       repo_init_revisions(the_repository, &rev_info, NULL);
+       repo_init_revisions(r, &rev_info, NULL);
        if (ignore_submodules) {
                rev_info.diffopt.flags.ignore_submodules = 1;
                rev_info.diffopt.flags.override_submodule_config = 1;
@@ -2326,15 +2333,16 @@ int has_unstaged_changes(int ignore_submodules)
 /**
  * Returns 1 if there are uncommitted changes, 0 otherwise.
  */
-int has_uncommitted_changes(int ignore_submodules)
+int has_uncommitted_changes(struct repository *r,
+                           int ignore_submodules)
 {
        struct rev_info rev_info;
        int result;
 
-       if (is_cache_unborn())
+       if (is_index_unborn(r->index))
                return 0;
 
-       repo_init_revisions(the_repository, &rev_info, NULL);
+       repo_init_revisions(r, &rev_info, NULL);
        if (ignore_submodules)
                rev_info.diffopt.flags.ignore_submodules = 1;
        rev_info.diffopt.flags.quick = 1;
@@ -2345,7 +2353,7 @@ int has_uncommitted_changes(int ignore_submodules)
                 * We have no head (or it's corrupt); use the empty tree,
                 * which will complain if the index is non-empty.
                 */
-               struct tree *tree = lookup_tree(the_repository, the_hash_algo->empty_tree);
+               struct tree *tree = lookup_tree(r, the_hash_algo->empty_tree);
                add_pending_object(&rev_info, &tree->object, "");
        }
 
@@ -2358,24 +2366,28 @@ int has_uncommitted_changes(int ignore_submodules)
  * If the work tree has unstaged or uncommitted changes, dies with the
  * appropriate message.
  */
-int require_clean_work_tree(const char *action, const char *hint, int ignore_submodules, int gently)
+int require_clean_work_tree(struct repository *r,
+                           const char *action,
+                           const char *hint,
+                           int ignore_submodules,
+                           int gently)
 {
        struct lock_file lock_file = LOCK_INIT;
        int err = 0, fd;
 
        fd = hold_locked_index(&lock_file, 0);
-       refresh_cache(REFRESH_QUIET);
+       refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
        if (0 <= fd)
-               update_index_if_able(&the_index, &lock_file);
+               update_index_if_able(r->index, &lock_file);
        rollback_lock_file(&lock_file);
 
-       if (has_unstaged_changes(ignore_submodules)) {
+       if (has_unstaged_changes(r, ignore_submodules)) {
                /* TRANSLATORS: the action is e.g. "pull with rebase" */
                error(_("cannot %s: You have unstaged changes."), _(action));
                err = 1;
        }
 
-       if (has_uncommitted_changes(ignore_submodules)) {
+       if (has_uncommitted_changes(r, ignore_submodules)) {
                if (err)
                        error(_("additionally, your index contains uncommitted changes."));
                else
index 1fcf93afbf57a9a6384b6be485846cd35fb3f465..3a95975032bb1ee8d87cca4e8a1bbc0afe62e42c 100644 (file)
@@ -7,6 +7,7 @@
 #include "pathspec.h"
 #include "remote.h"
 
+struct repository;
 struct worktree;
 
 enum color_wt_status {
@@ -83,6 +84,7 @@ struct wt_status_state {
 };
 
 struct wt_status {
+       struct repository *repo;
        int is_initial;
        char *branch;
        const char *reference;
@@ -128,11 +130,13 @@ struct wt_status {
 
 size_t wt_status_locate_end(const char *s, size_t len);
 void wt_status_add_cut_line(FILE *fp);
-void wt_status_prepare(struct wt_status *s);
+void wt_status_prepare(struct repository *r, struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
 void wt_status_collect_free_buffers(struct wt_status *s);
-void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
+void wt_status_get_state(struct repository *repo,
+                        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,
@@ -144,9 +148,14 @@ __attribute__((format (printf, 3, 4)))
 void status_printf(struct wt_status *s, const char *color, const char *fmt, ...);
 
 /* The following functions expect that the caller took care of reading the index. */
-int has_unstaged_changes(int ignore_submodules);
-int has_uncommitted_changes(int ignore_submodules);
-int require_clean_work_tree(const char *action, const char *hint,
-       int ignore_submodules, int gently);
+int has_unstaged_changes(struct repository *repo,
+                        int ignore_submodules);
+int has_uncommitted_changes(struct repository *repo,
+                           int ignore_submodules);
+int require_clean_work_tree(struct repository *repo,
+                           const char *action,
+                           const char *hint,
+                           int ignore_submodules,
+                           int gently);
 
 #endif /* STATUS_H */