]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jt/upload-pack-v2-fix-shallow'
authorJunio C Hamano <gitster@pobox.com>
Tue, 6 Nov 2018 06:50:19 +0000 (15:50 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 6 Nov 2018 06:50:19 +0000 (15:50 +0900)
"git fetch" over protocol v2 into a shallow repository failed to
fetch full history behind a new tip of history that was diverged
before the cut-off point of the history that was previously fetched
shallowly.

* jt/upload-pack-v2-fix-shallow:
  upload-pack: clear flags before each v2 request
  upload-pack: make want_obj not global
  upload-pack: make have_obj not global

268 files changed:
.clang-format
.editorconfig [new file with mode: 0644]
.gitignore
Documentation/CodingGuidelines
Documentation/RelNotes/2.20.0.txt
Documentation/config.txt
Documentation/diff-config.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-checkout.txt
Documentation/git-diff.txt
Documentation/git-gc.txt
Documentation/git-grep.txt
Documentation/git-help.txt
Documentation/git-merge-base.txt
Documentation/git-mergetool.txt
Documentation/git-p4.txt
Documentation/git-rebase.txt
Documentation/git-send-email.txt
Documentation/git-show-branch.txt
Documentation/git-tag.txt
Documentation/git-update-ref.txt
Documentation/git-upload-pack.txt
Documentation/git-worktree.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitmodules.txt
Documentation/gitsubmodules.txt
Documentation/gitweb.conf.txt
Documentation/howto/update-hook-example.txt
Documentation/howto/using-merge-subtree.txt
Documentation/merge-config.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/technical/api-diff.txt
Documentation/technical/api-revision-walking.txt
Documentation/technical/index-format.txt
Makefile
alias.h
apply.c
archive-zip.c
archive.c
archive.h
bisect.c
blame.c
builtin.h
builtin/add.c
builtin/am.c
builtin/archive.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/commit-graph.c
builtin/commit.c
builtin/describe.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/difftool.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/grep.c
builtin/hash-object.c
builtin/help.c
builtin/log.c
builtin/ls-remote.c
builtin/merge-base.c
builtin/merge-tree.c
builtin/merge.c
builtin/mktree.c
builtin/pack-objects.c
builtin/prune.c
builtin/pull.c
builtin/range-diff.c
builtin/rebase--helper.c [deleted file]
builtin/rebase--interactive.c [new file with mode: 0644]
builtin/rebase.c [new file with mode: 0644]
builtin/receive-pack.c
builtin/reflog.c
builtin/repack.c
builtin/replace.c
builtin/rerere.c
builtin/reset.c
builtin/rev-list.c
builtin/revert.c
builtin/shortlog.c
builtin/submodule--helper.c
builtin/update-index.c
builtin/upload-archive.c
bundle.c
cache-tree.c
cache.h
ci/run-build-and-tests.sh
combine-diff.c
command-list.txt
commit-graph.c
commit-reach.c
commit-reach.h
commit-slab-impl.h
commit.c
commit.h
compat/mingw.c
compat/mingw.h
compat/mmap.c
config.c
config.h
config.mak.dev
connected.h
contrib/coccinelle/object_id.cocci
contrib/coccinelle/preincr.cocci [new file with mode: 0644]
contrib/completion/git-completion.bash
contrib/coverage-diff.sh [new file with mode: 0755]
contrib/credential/wincred/git-credential-wincred.c
contrib/subtree/Makefile
contrib/subtree/git-subtree.sh
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-break.c
diffcore-delta.c
diffcore-pickaxe.c
diffcore-rename.c
diffcore.h
editor.c
fetch-negotiator.h
fetch-pack.c
fetch-pack.h
fsck.c
fuzz-pack-headers.c [new file with mode: 0644]
fuzz-pack-idx.c [new file with mode: 0644]
git-compat-util.h
git-legacy-rebase.sh [moved from git-rebase.sh with 89% similarity]
git-mergetool--lib.sh
git-mergetool.sh
git-p4.py
git-rebase--common.sh [new file with mode: 0644]
git-rebase--interactive.sh [deleted file]
git-rebase--preserve-merges.sh
git-send-email.perl
git-submodule.sh
git.c
gpg-interface.c
gpg-interface.h
graph.c
grep.c
grep.h
help.c
http-push.c
http.c
ident.c
khash.h
line-log.c
line-range.c
line-range.h
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
merge-blobs.c
merge-blobs.h
merge-recursive.c
merge.c
midx.c
midx.h
notes-merge.c
oidset.c
oidset.h
pack-bitmap-write.c
pack-objects.c
pack-objects.h
pack-revindex.c
packfile.c
packfile.h
patch-ids.c
patch-ids.h
preload-index.c
pretty.c
range-diff.c
read-cache.c
rebase-interactive.c [new file with mode: 0644]
rebase-interactive.h [new file with mode: 0644]
ref-filter.c
refs/packed-backend.c
remote.c
rerere.c
rerere.h
revision.c
revision.h
run-command.c
sequencer.c
sequencer.h
sha1-file.c
shallow.c
split-index.c
strbuf.c
strbuf.h
submodule.c
submodule.h
t/README
t/helper/test-revision-walking.c
t/helper/test-tool.c
t/helper/test-tool.h
t/lib-rebase.sh
t/t0000-basic.sh
t/t0012-help.sh
t/t0061-run-command.sh
t/t0410-partial-clone.sh
t/t1090-sparse-checkout-scope.sh
t/t1500-rev-parse.sh
t/t1700-split-index.sh
t/t1701-racy-split-index.sh [new file with mode: 0755]
t/t2000-checkout-cache-clash.sh [deleted file]
t/t2000-conflict-when-checking-files-out.sh [new file with mode: 0755]
t/t2001-checkout-cache-clash.sh [deleted file]
t/t3206-range-diff.sh
t/t3404-rebase-interactive.sh
t/t3418-rebase-continue.sh
t/t3420-rebase-autostash.sh
t/t4053-diff-no-index.sh
t/t4214-log-graph-octopus.sh [new file with mode: 0755]
t/t5000-tar-tree.sh
t/t5003-archive-zip.sh
t/t5317-pack-objects-filter-objects.sh
t/t5321-pack-large-objects.sh [new file with mode: 0755]
t/t5410-receive-pack-alternates.sh [new file with mode: 0755]
t/t5516-fetch-push.sh
t/t5537-fetch-shallow.sh
t/t5551-http-fetch-smart.sh
t/t5616-partial-clone.sh
t/t5702-protocol-v2.sh
t/t6036-recursive-corner-cases.sh
t/t6112-rev-list-filters-objects.sh
t/t7005-editor.sh
t/t7400-submodule-basic.sh
t/t7500-commit-template-squash-signoff.sh [moved from t/t7500-commit.sh with 99% similarity]
t/t7501-commit-basic-functionality.sh [moved from t/t7501-commit.sh with 98% similarity]
t/t7502-commit-porcelain.sh [moved from t/t7502-commit.sh with 100% similarity]
t/t7509-commit-authorship.sh [moved from t/t7509-commit.sh with 98% similarity]
t/t7510-signed-commit.sh
t/t7519-status-fsmonitor.sh
t/t7810-grep.sh
t/t9832-unshelve.sh
t/test-lib.sh
tag.c
transport-helper.c
transport-internal.h
transport.c
transport.h
tree-diff.c
unpack-trees.c
upload-pack.c
userdiff.c
userdiff.h
vcs-svn/fast_export.h
vcs-svn/line_buffer.h
vcs-svn/sliding_window.h
vcs-svn/svndiff.h
vcs-svn/svndump.h
ws.c
wt-status.c
wt-status.h

index 12a89f95f993546888410613458c9385b16f0108..de1c8b5c77f7566d9e41949e5e397db3cc1b487c 100644 (file)
@@ -6,6 +6,8 @@
 
 # Use tabs whenever we need to fill whitespace that spans at least from one tab
 # stop to the next one.
+#
+# These settings are mirrored in .editorconfig.  Keep them in sync.
 UseTab: Always
 TabWidth: 8
 IndentWidth: 8
diff --git a/.editorconfig b/.editorconfig
new file mode 100644 (file)
index 0000000..42cdc4b
--- /dev/null
@@ -0,0 +1,16 @@
+[*]
+charset = utf-8
+insert_final_newline = true
+
+# The settings for C (*.c and *.h) files are mirrored in .clang-format.  Keep
+# them in sync.
+[*.{c,h,sh,perl,pl,pm}]
+indent_style = tab
+tab_width = 8
+
+[*.py]
+indent_style = space
+indent_size = 4
+
+[COMMIT_EDITMSG]
+max_line_length = 72
index 9d1363a1ebce8432c15f610aa7af9520e3e2bb12..0d77ea5894274c43c4b348c8b52b8e665a1a339e 100644 (file)
@@ -1,3 +1,6 @@
+/fuzz_corpora
+/fuzz-pack-headers
+/fuzz-pack-idx
 /GIT-BUILD-OPTIONS
 /GIT-CFLAGS
 /GIT-LDFLAGS
@@ -78,6 +81,7 @@
 /git-init-db
 /git-interpret-trailers
 /git-instaweb
+/git-legacy-rebase
 /git-log
 /git-ls-files
 /git-ls-remote
 /git-read-tree
 /git-rebase
 /git-rebase--am
-/git-rebase--helper
+/git-rebase--common
 /git-rebase--interactive
 /git-rebase--merge
 /git-rebase--preserve-merges
index 72967deb785814546830cf23a54eeeb93d147681..8579530710a7e2b665941be45264429b99d5134b 100644 (file)
@@ -376,7 +376,10 @@ For C programs:
    string_list for sorted string lists, a hash map (mapping struct
    objects) named "struct decorate", amongst other things.
 
- - When you come up with an API, document it.
+ - When you come up with an API, document its functions and structures
+   in the header file that exposes the API to its callers. Use what is
+   in "strbuf.h" as a model for the appropriate tone and level of
+   detail.
 
  - The first #include in C files, except in platform specific compat/
    implementations, must be either "git-compat-util.h", "cache.h" or
index fa16dda07a6ea4303d987435fcf9e2c495182e1c..4b546d025f3a7e84c0743df486aa513ea0192069 100644 (file)
@@ -14,6 +14,21 @@ Backward Compatibility Notes
    which means some fetches of tags that did not fail with older
    version of Git will fail without "--force" with this version.
 
+ * "git help -a" now gives verbose output (same as "git help -av").
+   Those who want the old output may say "git help --no-verbose -a"..
+
+ * "git cpn --help", when "cpn" is an alias to, say, "cherry-pick -n",
+   reported only the alias expansion of "cpn" in earlier versions of
+   Git.  It now runs "git cherry-pick --help" to show the manual page
+   of the command, while sending the alias expansion to the standard
+   error stream.
+
+ * "git send-email" learned to grab address-looking string on any
+   trailer whose name ends with "-by". This is a backward-incompatible
+   change.  Adding "--suppress-cc=misc-by" on the command line, or
+   setting sendemail.suppresscc configuration variable to "misc-by",
+   can be used to disable this behaviour.
+
 
 Updates since v2.19
 -------------------
@@ -69,6 +84,71 @@ UI, Workflows & Features
  * The completion script (in contrib/) learned to complete a handful of
    options "git stash list" command takes.
 
+ * The completion script (in contrib/) learned that "git fetch
+   --multiple" only takes remote names as arguments and no refspecs.
+
+ * "git status" learns to show progress bar when refreshing the index
+   takes a long time.
+   (merge ae9af12287 nd/status-refresh-progress later to maint).
+
+ * "git help -a" and "git help -av" give different pieces of
+   information, and generally the "verbose" version is more friendly
+   to the new users.  "git help -a" by default now uses the more
+   verbose output (with "--no-verbose", you can go back to the
+   original).  Also "git help -av" now lists aliases and external
+   commands, which it did not used to.
+
+ * Unlike "grep", "git grep" by default recurses to the whole tree.
+   The command learned "git grep --recursive" option, so that "git
+   grep --no-recursive" can serve as a synonym to setting the
+   max-depth to 0.
+
+ * When pushing into a repository that borrows its objects from an
+   alternate object store, "git receive-pack" that responds to the
+   push request on the other side lists the tips of refs in the
+   alternate to reduce the amount of objects transferred.  This
+   sometimes is detrimental when the number of refs in the alternate
+   is absurdly large, in which case the bandwidth saved in potentially
+   fewer objects transferred is wasted in excessively large ref
+   advertisement.  The alternate refs that are advertised are now
+   configurable with a pair of configuration variables.
+
+ * "git cmd --help" when "cmd" is aliased used to only say "cmd is
+   aliased to ...".  Now it shows that to the standard error stream
+   and runs "git $cmd --help" where $cmd is the first word of the
+   alias expansion.
+
+ * The documentation of "git gc" has been updated to mention that it
+   is no longer limited to "pruning away crufts" but also updates
+   ancillary files like commit-graph as a part of repository
+   optimization.
+
+ * "git p4 unshelve" improvements.
+
+ * The logic to select the default user name and e-mail on Windows has
+   been improved.
+   (merge 501afcb8b0 js/mingw-default-ident later to maint).
+
+ * The "rev-list --filter" feature learned to exclude all trees via
+   "tree:0" filter.
+
+ * "git send-email" learned to grab address-looking string on any
+   trailer whose name ends with "-by"; --suppress-cc=misc-by on the
+   command line, or setting sendemail.suppresscc configuration
+   variable to "misc-by", can be used to disable this behaviour.
+
+ * Developer builds now uses -Wunused-function compilation option.
+
+ * One of our CI tests to run with "unusual/experimental/random"
+   settings now also uses commit-graph and midx.
+
+ * "git mergetool" learned to take the "--[no-]gui" option, just like
+   "git difftool" does.
+
+ * "git rebase -i" learned a new insn, 'break', that the user can
+   insert in the to-do list.  Upon hitting it, the command returns
+   control back to the user.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -132,6 +212,62 @@ Performance, Internal Implementation, Development Support etc.
    point running gc to improve the situation); we used to exit with
    failure in such a case.
 
+ * Various codepaths in the core-ish part learned to work on an
+   arbitrary in-core index structure, not necessarily the default
+   instance "the_index".
+   (merge b3c7eef9b0 nd/the-index later to maint).
+
+ * Code clean-up in the internal machinery used by "git status" and
+   "git commit --dry-run".
+   (merge 73ba5d78b4 ss/wt-status-committable later to maint).
+
+ * Some environment variables that control the runtime options of Git
+   used during tests are getting renamed for consistency.
+   (merge 4231d1ba99 bp/rename-test-env-var later to maint).
+
+ * A new extension to the index file has been introduced, which allows
+   the index file to be read in parallel for performance.
+
+ * The oidset API was built on top of the oidmap API which in turn is
+   on the hashmap API.  Replace the implementation to build on top of
+   the khash API and gain performance.
+
+ * Over some transports, fetching objects with an exact commit object
+   name can be done without first seeing the ref advertisements.  The
+   code has been optimized to exploit this.
+
+ * In a partial clone that will lazily be hydrated from the
+   originating repository, we generally want to avoid "does this
+   object exist (locally)?" on objects that we deliberately omitted
+   when we created the clone.  The cache-tree codepath (which is used
+   to write a tree object out of the index) however insisted that the
+   object exists, even for paths that are outside of the partial
+   checkout area.  The code has been updated to avoid such a check.
+
+ * To help developers, an EditorConfig file that attempts to follow
+   the project convention has been added.
+   (merge b548d698a0 bc/editorconfig later to maint).
+
+ * The result of coverage test can be combined with "git blame" to
+   check the test coverage of code introduced recently with a new
+   'coverage-diff' tool (in contrib/).
+   (merge 783faedd65 ds/coverage-diff later to maint).
+
+ * An experiment to fuzz test a few areas, hopefully we can gain more
+   coverage to various areas.
+
+ * More codepaths are moving away from hardcoded hash sizes.
+
+ * The way the Windows port figures out the current directory has been
+   improved.
+
+ * The way DLLs are loaded on the Windows port has been improved.
+
+ * Some tests have been reorganized and renamed; "ls t/" now gives a
+   better overview of what is tested for these scripts than before.
+
+ * "git rebase" and "git rebase -i" have been reimplemented in C.
+
 
 Fixes since v2.19
 -----------------
@@ -209,6 +345,55 @@ Fixes since v2.19
    used for the first run, which has been corrected.
    (merge 3e73cc62c0 en/status-multiple-renames-to-the-same-target-fix later to maint).
 
+ * "git fetch $repo $object" in a partial clone did not correctly
+   fetch the asked-for object that is referenced by an object in
+   promisor packfile, which has been fixed.
+
+ * A corner-case bugfix.
+   (merge c5cbb27cb5 sm/show-superproject-while-conflicted later to maint).
+
+ * Various fixes to "diff --color-moved-ws".
+
+ * A partial clone that is configured to lazily fetch missing objects
+   will on-demand issue a "git fetch" request to the originating
+   repository to fill not-yet-obtained objects.  The request has been
+   optimized for requesting a tree object (and not the leaf blob
+   objects contained in it) by telling the originating repository that
+   no blobs are needed.
+   (merge 4c7f9567ea jt/non-blob-lazy-fetch later to maint).
+
+ * The codepath to support the experimental split-index mode had
+   remaining "racily clean" issues fixed.
+   (merge 4c490f3d32 sg/split-index-racefix later to maint).
+
+ * "git log --graph" showing an octopus merge sometimes miscounted the
+   number of display columns it is consuming to show the merge and its
+   parent commits, which has been corrected.
+   (merge 04005834ed np/log-graph-octopus-fix later to maint).
+
+ * "git range-diff" did not work well when the compared ranges had
+   changes in submodules and the "--submodule=log" was used.
+
+ * The implementation of run_command() API on the UNIX platforms had a
+   bug that caused a command not on $PATH to be found in the current
+   directory.
+   (merge f67b980771 jk/run-command-notdot later to maint).
+
+ * A mutex used in "git pack-objects" were not correctly initialized
+   and this caused "git repack" to dump core on Windows.
+   (merge 34204c8166 js/pack-objects-mutex-init-fix later to maint).
+
+ * Under certain circumstances, "git diff D:/a/b/c D:/a/b/d" on
+   Windows would strip initial parts from the paths because they
+   were not recognized as absolute, which has been corrected.
+   (merge ffd04e92e2 js/diff-notice-has-drive-prefix later to maint).
+
+ * The receive.denyCurrentBranch=updateInstead codepath kicked in even
+   when the push should have been rejected due to other reasons, such
+   as it does not fast-forward or the update-hook rejects it, which
+   has been corrected.
+   (merge b072a25fad jc/receive-deny-current-branch-fix later to maint).
+
  * Code cleanup, docfix, build fix, etc.
    (merge 96a7501aad ts/doc-build-manpage-xsl-quietly later to maint).
    (merge b9b07efdb2 tg/conflict-marker-size later to maint).
@@ -231,3 +416,17 @@ Fixes since v2.19
    (merge c56170a0c4 ma/mailing-list-address-in-git-help later to maint).
    (merge 6e8fc70fce rs/sequencer-oidset-insert-avoids-dups later to maint).
    (merge ad0b8f9575 mw/doc-typofixes later to maint).
+   (merge d9f079ad1a jc/how-to-document-api later to maint).
+   (merge b1492bf315 ma/t7005-bash-workaround later to maint).
+   (merge ac1f98a0df du/rev-parse-is-plumbing later to maint).
+   (merge ca8ed443a5 mm/doc-no-dashed-git later to maint).
+   (merge ce366a8144 du/get-tar-commit-id-is-plumbing later to maint).
+   (merge 61018fe9e0 du/cherry-is-plumbing later to maint).
+   (merge c7e5fe79b9 sb/strbuf-h-update later to maint).
+   (merge 8d2008196b tq/branch-create-wo-branch-get later to maint).
+   (merge 2e3c894f4b tq/branch-style-fix later to maint).
+   (merge c5d844af9c sg/doc-show-branch-typofix later to maint).
+   (merge 081d91618b ah/doc-updates later to maint).
+   (merge b84c783882 jc/cocci-preincr later to maint).
+   (merge 5e495f8122 uk/merge-subtree-doc-update later to maint).
+   (merge aaaa881822 jk/uploadpack-packobjectshook-fix later to maint).
index f6f4c21a54579934ef8a413c4f77787911ed14c9..41a9ff2b6aa4ebcc87179ede7a70f680e2eba439 100644 (file)
@@ -616,6 +616,24 @@ core.preferSymlinkRefs::
        This is sometimes needed to work with old scripts that
        expect HEAD to be a symbolic link.
 
+core.alternateRefsCommand::
+       When advertising tips of available history from an alternate, use the shell to
+       execute the specified command instead of linkgit:git-for-each-ref[1]. The
+       first argument is the absolute path of the alternate. Output must contain one
+       hex object id per line (i.e., the same as produced by `git for-each-ref
+       --format='%(objectname)'`).
++
+Note that you cannot generally put `git for-each-ref` directly into the config
+value, as it does not take a repository path as an argument (but you can wrap
+the command above in a shell script).
+
+core.alternateRefsPrefixes::
+       When listing references from an alternate, list only references that begin
+       with the given prefix. Prefixes match as if they were given as arguments to
+       linkgit:git-for-each-ref[1]. To list multiple prefixes, separate them with
+       whitespace. If `core.alternateRefsCommand` is set, setting
+       `core.alternateRefsPrefixes` has no effect.
+
 core.bare::
        If true this repository is assumed to be 'bare' and has no
        working directory associated with it.  If this is the case a
@@ -1985,6 +2003,27 @@ http.sslCAPath::
        with when fetching or pushing over HTTPS. Can be overridden
        by the `GIT_SSL_CAPATH` environment variable.
 
+http.sslBackend::
+       Name of the SSL backend to use (e.g. "openssl" or "schannel").
+       This option is ignored if cURL lacks support for choosing the SSL
+       backend at runtime.
+
+http.schannelCheckRevoke::
+       Used to enforce or disable certificate revocation checks in cURL
+       when http.sslBackend is set to "schannel". Defaults to `true` if
+       unset. Only necessary to disable this if Git consistently errors
+       and the message is about checking the revocation status of a
+       certificate. This option is ignored if cURL lacks support for
+       setting the relevant SSL option at runtime.
+
+http.schannelUseSSLCAInfo::
+       As of cURL v7.60.0, the Secure Channel backend can use the
+       certificate bundle provided via `http.sslCAInfo`, but that would
+       override the Windows Certificate Store. Since this is not desirable
+       by default, Git will tell cURL not to use that bundle by default
+       when the `schannel` backend was configured via `http.sslBackend`,
+       unless `http.schannelUseSSLCAInfo` overrides this behavior.
+
 http.pinnedpubkey::
        Public key of the https service. It may either be the filename of
        a PEM or DER encoded public key file or a string starting with
@@ -2149,6 +2188,13 @@ imap::
        The configuration variables in the 'imap' section are described
        in linkgit:git-imap-send[1].
 
+index.threads::
+       Specifies the number of threads to spawn when loading the index.
+       This is meant to reduce index load time on multiprocessor machines.
+       Specifying 0 or 'true' will cause Git to auto-detect the number of
+       CPU's and set the number of threads accordingly. Specifying 1 or
+       'false' will disable multithreading. Defaults to 'true'.
+
 index.version::
        Specify the version with which new index files should be
        initialized.  This does not affect existing repositories.
index 85bca83c304c6d95422482b9bc7b8d983bfdc85e..e64d983c344656dd00d869005563a1a20efce854 100644 (file)
@@ -177,6 +177,14 @@ diff.tool::
        Any other value is treated as a custom diff tool and requires
        that a corresponding difftool.<tool>.cmd variable is defined.
 
+diff.guitool::
+       Controls which diff tool is used by linkgit:git-difftool[1] when
+       the -g/--gui flag is specified. This variable overrides the value
+       configured in `merge.guitool`. The list below shows the valid
+       built-in values. Any other value is treated as a custom diff tool
+       and requires that a corresponding difftool.<guitool>.cmd variable
+       is defined.
+
 include::mergetools-diff.txt[]
 
 diff.indentHeuristic::
index 0f9ef2f25e039e2a9ce72988962377439e84590c..e99925184d031cac622395386ab6255e024bdff8 100644 (file)
@@ -633,11 +633,11 @@ and so at step 3) we compute f(X).
 Let's take the following graph as an example:
 
 -------------
-           G-H-I-J
-          /       \
+            G-H-I-J
+           /       \
 A-B-C-D-E-F         O
-          \       /
-           K-L-M-N
+           \       /
+            K-L-M-N
 -------------
 
 If we compute the following non optimal function on it:
@@ -649,25 +649,25 @@ g(X) = min(number_of_ancestors(X), number_of_descendants(X))
 we get:
 
 -------------
-           4 3 2 1
-           G-H-I-J
+            4 3 2 1
+            G-H-I-J
 1 2 3 4 5 6/       \0
 A-B-C-D-E-F         O
-          \       /
-           K-L-M-N
-           4 3 2 1
+           \       /
+            K-L-M-N
+            4 3 2 1
 -------------
 
 but with the algorithm used by git bisect we get:
 
 -------------
-           7 7 6 5
-           G-H-I-J
+            7 7 6 5
+            G-H-I-J
 1 2 3 4 5 6/       \0
 A-B-C-D-E-F         O
-          \       /
-           K-L-M-N
-           7 7 6 5
+           \       /
+            K-L-M-N
+            7 7 6 5
 -------------
 
 So we chose G, H, K or L as the best bisection point, which is better
@@ -773,7 +773,7 @@ forked of the main branch at a commit named "D" like this:
 -------------
 A-B-C-D-E-F-G  <--main
        \
-       H-I-J  <--dev
+        H-I-J  <--dev
 -------------
 
 The commit "D" is called a "merge base" for branch "main" and "dev"
index 9db02928c4634ea07c3950b1431bdf9981ec1ab9..801de2f7645bf0b1fc506da58d469519abca8863 100644 (file)
@@ -311,9 +311,9 @@ branch refers to a specific commit. Let's look at a repo with three
 commits, one of them tagged, and with branch 'master' checked out:
 
 ------------
-          HEAD (refers to branch 'master')
-           |
-           v
+           HEAD (refers to branch 'master')
+            |
+            v
 a---b---c  branch 'master' (refers to commit 'c')
     ^
     |
@@ -329,9 +329,9 @@ to commit 'd':
 ------------
 $ edit; git add; git commit
 
-              HEAD (refers to branch 'master')
-               |
-               v
+               HEAD (refers to branch 'master')
+                |
+                v
 a---b---c---d  branch 'master' (refers to commit 'd')
     ^
     |
@@ -398,7 +398,7 @@ at what happens when we then checkout master:
 ------------
 $ git checkout master
 
-              HEAD (refers to branch 'master')
+               HEAD (refers to branch 'master')
       e---f     |
      /          v
 a---b---c---d  branch 'master' (refers to commit 'd')
index b180f1fa5bf5599efd8c8fefe29e3249cd387715..030f162f301608c329fecae04953620eb78f0b14 100644 (file)
@@ -72,10 +72,10 @@ two blob objects, or changes between two files on disk.
        This form is to view the changes on the branch containing
        and up to the second <commit>, starting at a common ancestor
        of both <commit>.  "git diff A\...B" is equivalent to
-       "git diff $(git-merge-base A B) B".  You can omit any one
+       "git diff $(git merge-base A B) B".  You can omit any one
        of <commit>, which has the same effect as using HEAD instead.
 
-Just in case if you are doing something exotic, it should be
+Just in case you are doing something exotic, it should be
 noted that all of the <commit> in the above description, except
 in the last two forms that use ".." notations, can be any
 <tree>.
index f5bc98ccb3673079fa2e1205b57bc87acb3c1e90..c20ee6c7892518de4ba915e473e7ed736f68fe6b 100644 (file)
@@ -17,7 +17,8 @@ Runs a number of housekeeping tasks within the current repository,
 such as compressing file revisions (to reduce disk space and increase
 performance), removing unreachable objects which may have been
 created from prior invocations of 'git add', packing refs, pruning
-reflog, rerere metadata or stale working trees.
+reflog, rerere metadata or stale working trees. May also update ancillary
+indexes such as the commit-graph.
 
 Users are encouraged to run this task on a regular basis within
 each repository to maintain good disk space utilization and good
index a3049af1a36c09325a88ad2cc822ae2891c8974b..84fe236a8ee2fd47814c4c7ee854c47279921169 100644 (file)
@@ -18,7 +18,7 @@ SYNOPSIS
           [(-O | --open-files-in-pager) [<pager>]]
           [-z | --null]
           [ -o | --only-matching ] [-c | --count] [--all-match] [-q | --quiet]
-          [--max-depth <depth>]
+          [--max-depth <depth>] [--[no-]recursive]
           [--color[=<when>] | --no-color]
           [--break] [--heading] [-p | --show-function]
           [-A <post-context>] [-B <pre-context>] [-C <context>]
@@ -119,11 +119,18 @@ OPTIONS
 
 --max-depth <depth>::
        For each <pathspec> given on command line, descend at most <depth>
-       levels of directories. A negative value means no limit.
+       levels of directories. A value of -1 means no limit.
        This option is ignored if <pathspec> contains active wildcards.
        In other words if "a*" matches a directory named "a*",
        "*" is matched literally so --max-depth is still effective.
 
+-r::
+--recursive::
+       Same as `--max-depth=-1`; this is the default.
+
+--no-recursive::
+       Same as `--max-depth=0`.
+
 -w::
 --word-regexp::
        Match the pattern only at word boundary (either begin at the
index 83d25d825aa5a9b68f3cff5290d1ebacef35ebde..aab5453bbbb239931ee2006095135c8db9f0f9ea 100644 (file)
@@ -8,7 +8,7 @@ git-help - Display help information about Git
 SYNOPSIS
 --------
 [verse]
-'git help' [-a|--all [--verbose]] [-g|--guide]
+'git help' [-a|--all [--[no-]verbose]] [-g|--guide]
           [-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE]
 
 DESCRIPTION
@@ -29,6 +29,10 @@ guide is brought up. The 'man' program is used by default for this
 purpose, but this can be overridden by other options or configuration
 variables.
 
+If an alias is given, git shows the definition of the alias on
+standard output. To get the manual page for the aliased command, use
+`git COMMAND --help`.
+
 Note that `git --help ...` is identical to `git help ...` because the
 former is internally converted into the latter.
 
@@ -42,8 +46,10 @@ OPTIONS
 --all::
        Prints all the available commands on the standard output. This
        option overrides any given command or guide name.
-       When used with `--verbose` print description for all recognized
-       commands.
+
+--verbose::
+       When used with `--all` print description for all recognized
+       commands. This is the default.
 
 -c::
 --config::
index 502e00ec35ff1fd543dbd8361a317cb583ca1eba..9f07f4f6ed7f5036578f1cabb788f900247761c5 100644 (file)
@@ -154,13 +154,13 @@ topic origin/master`, the history of remote-tracking branch
 `origin/master` may have been rewound and rebuilt, leading to a
 history of this shape:
 
-                        o---B2
+                        o---B2
                        /
        ---o---o---B1--o---o---o---B (origin/master)
                \
-                B0
+                B0
                  \
-                  D0---D1---D (topic)
+                  D0---D1---D (topic)
 
 where `origin/master` used to point at commits B0, B1, B2 and now it
 points at B, and your `topic` branch was started on top of it back
index 3622d66488c7de057d32e02cd4cfd21704500822..0c7975a0507a35f9624516f0844ad11cd5141d50 100644 (file)
@@ -79,6 +79,17 @@ success of the resolution after the custom tool has exited.
        Prompt before each invocation of the merge resolution program
        to give the user a chance to skip the path.
 
+-g::
+--gui::
+       When 'git-mergetool' is invoked with the `-g` or `--gui` option
+       the default merge tool will be read from the configured
+       `merge.guitool` variable instead of `merge.tool`.
+
+--no-gui::
+       This overrides a previous `-g` or `--gui` setting and reads the
+       default merge tool will be read from the configured `merge.tool`
+       variable.
+
 -O<orderfile>::
        Process files in the order specified in the
        <orderfile>, which has one shell glob pattern per line.
index 41780a5aa970fb46538373afbf6684a526803f63..f0a0280954f18532a1f12d4e5e6cae094c778784 100644 (file)
@@ -174,21 +174,21 @@ $ git p4 submit --update-shelve 1234 --update-shelve 2345
 Unshelve
 ~~~~~~~~
 Unshelving will take a shelved P4 changelist, and produce the equivalent git commit
-in the branch refs/remotes/p4/unshelved/<changelist>.
+in the branch refs/remotes/p4-unshelved/<changelist>.
 
 The git commit is created relative to the current origin revision (HEAD by default).
-If the shelved changelist's parent revisions differ, git-p4 will refuse to unshelve;
-you need to be unshelving onto an equivalent tree.
+A parent commit is created based on the origin, and then the unshelve commit is
+created based on that.
 
 The origin revision can be changed with the "--origin" option.
 
-If the target branch in refs/remotes/p4/unshelved already exists, the old one will
+If the target branch in refs/remotes/p4-unshelved already exists, the old one will
 be renamed.
 
 ----
 $ git p4 sync
 $ git p4 unshelve 12345
-$ git show refs/remotes/p4/unshelved/12345
+$ git show p4-unshelved/12345
 <submit more changes via p4 to the same files>
 $ git p4 unshelve 12345
 <refuses to unshelve until git is in sync with p4 again>
index 432baabe33e50d5e6f582aea8a9dee1e6de58fc8..3407d835bdb233ce6b1779bd33d91767d88a9716 100644 (file)
@@ -441,7 +441,8 @@ See also INCOMPATIBLE OPTIONS below.
 --exec <cmd>::
        Append "exec <cmd>" after each line creating a commit in the
        final history. <cmd> will be interpreted as one or more shell
-       commands.
+       commands. Any command that fails will interrupt the rebase,
+       with exit code 1.
 +
 You may execute several commands by either using one instance of `--exec`
 with several commands:
@@ -641,6 +642,9 @@ By replacing the command "pick" with the command "edit", you can tell
 the files and/or the commit message, amend the commit, and continue
 rebasing.
 
+To interrupt the rebase (just like an "edit" command would do, but without
+cherry-picking any commit first), use the "break" command.
+
 If you just want to edit the commit message for a commit, replace the
 command "pick" with the command "reword".
 
index 465a4ecbeddaa20189bce6e223df8bbfa0319b9a..f6010ac68b9a683fd9821b13ca10f52db00b096c 100644 (file)
@@ -321,16 +321,19 @@ Automating
        auto-cc of:
 +
 --
-- 'author' will avoid including the patch author
-- 'self' will avoid including the sender
+- 'author' will avoid including the patch author.
+- 'self' will avoid including the sender.
 - 'cc' will avoid including anyone mentioned in Cc lines in the patch header
   except for self (use 'self' for that).
 - 'bodycc' will avoid including anyone mentioned in Cc lines in the
   patch body (commit message) except for self (use 'self' for that).
 - 'sob' will avoid including anyone mentioned in Signed-off-by lines except
-   for self (use 'self' for that).
+  for self (use 'self' for that).
+- 'misc-by' will avoid including anyone mentioned in Acked-by,
+  Reviewed-by, Tested-by and other "-by" lines in the patch body,
+  except Signed-off-by (use 'sob' for that).
 - 'cccmd' will avoid running the --cc-cmd.
-- 'body' is equivalent to 'sob' + 'bodycc'
+- 'body' is equivalent to 'sob' + 'bodycc' + 'misc-by'.
 - 'all' will suppress all auto cc values.
 --
 +
index 262db049d772d4249983da5ca0d21d8f6b0f8d92..4a013712274f81d4ca594b06ae897db9e67ee827 100644 (file)
@@ -19,7 +19,7 @@ DESCRIPTION
 -----------
 
 Shows the commit ancestry graph starting from the commits named
-with <rev>s or <globs>s (or all refs under refs/heads
+with <rev>s or <glob>s (or all refs under refs/heads
 and/or refs/tags) semi-visually.
 
 It cannot show more than 29 branches and commits at a time.
index 92f9c12b873f92c9f66a460272ccc0d235a6cad9..f2d644e3af1849d842567bfb7a2c38083984545d 100644 (file)
@@ -187,6 +187,12 @@ This option is only applicable when listing tags without annotation lines.
        `--create-reflog`, but currently does not negate the setting of
        `core.logAllRefUpdates`.
 
+--format=<format>::
+       A string that interpolates `%(fieldname)` from a tag ref being shown
+       and the object it points at.  The format is the same as
+       that of linkgit:git-for-each-ref[1].  When unspecified,
+       defaults to `%(refname:strip=2)`.
+
 <tagname>::
        The name of the tag to create, delete, or describe.
        The new tag name must pass all checks defined by
@@ -198,12 +204,6 @@ This option is only applicable when listing tags without annotation lines.
        The object that the new tag will refer to, usually a commit.
        Defaults to HEAD.
 
-<format>::
-       A string that interpolates `%(fieldname)` from a tag ref being shown
-       and the object it points at.  The format is the same as
-       that of linkgit:git-for-each-ref[1].  When unspecified,
-       defaults to `%(refname:strip=2)`.
-
 CONFIGURATION
 -------------
 By default, 'git tag' in sign-with-default mode (-s) will use your
index fda8516677237e9c0a8343f9ca96a1eb969b3492..96714231179cac8242f1bd0f781503d607c4add3 100644 (file)
@@ -129,8 +129,8 @@ a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
 symbolic refs before creating the log name) describing the change
 in ref value.  Log lines are formatted as:
 
-    oldsha1 SP newsha1 SP committer LF
-+
+    oldsha1 SP newsha1 SP committer LF
+
 Where "oldsha1" is the 40 character hexadecimal value previously
 stored in <ref>, "newsha1" is the 40 character hexadecimal value of
 <newvalue> and "committer" is the committer's name, email address
@@ -138,8 +138,8 @@ and date in the standard Git committer ident format.
 
 Optionally with -m:
 
-    oldsha1 SP newsha1 SP committer TAB message LF
-+
+    oldsha1 SP newsha1 SP committer TAB message LF
+
 Where all fields are as described above and "message" is the
 value supplied to the -m option.
 
index 822ad593af973dd7a1217cffaef7106876d377c2..998f52d3df71e29d4178251de375d70cd570a1d8 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git-upload-pack' [--[no-]strict] [--timeout=<n>] [--stateless-rpc]
                  [--advertise-refs] <directory>
+
 DESCRIPTION
 -----------
 Invoked by 'git fetch-pack', learns what
index e2ee9fc21b400b01b006d5460698f0229e23ff9f..73520434f6b2dc4f07ac210bd50da8878c824ce5 100644 (file)
@@ -270,8 +270,8 @@ Porcelain Format
 The porcelain format has a line per attribute.  Attributes are listed with a
 label and value separated by a single space.  Boolean attributes (like 'bare'
 and 'detached') are listed as a label only, and are only present if and only
-if the value is true.  An empty line indicates the end of a worktree.  For
-example:
+if the value is true.  The first attribute of a worktree is always `worktree`,
+an empty line indicates the end of the record.  For example:
 
 ------------
 $ git worktree list --porcelain
index 2ac9b1c7fe00fea0772d5269121ffdf424435b96..00156d64aad51cd758e554ada68a52a3e55dfc0c 100644 (file)
@@ -402,11 +402,11 @@ Git so take care if using a foreign front-end.
        of Git object directories which can be used to search for Git
        objects. New objects will not be written to these directories.
 +
-       Entries that begin with `"` (double-quote) will be interpreted
-       as C-style quoted paths, removing leading and trailing
-       double-quotes and respecting backslash escapes. E.g., the value
-       `"path-with-\"-and-:-in-it":vanilla-path` has two paths:
-       `path-with-"-and-:-in-it` and `vanilla-path`.
+Entries that begin with `"` (double-quote) will be interpreted
+as C-style quoted paths, removing leading and trailing
+double-quotes and respecting backslash escapes. E.g., the value
+`"path-with-\"-and-:-in-it":vanilla-path` has two paths:
+`path-with-"-and-:-in-it` and `vanilla-path`.
 
 `GIT_DIR`::
        If the `GIT_DIR` environment variable is set then it
index 92010b062e08678fcfb0a143f75bbd4b07bfcf4b..b8392fc3300cfa7d7c37e37dd069246f20ceadb1 100644 (file)
@@ -303,21 +303,21 @@ number of pitfalls:
   attribute. If you decide to use the `working-tree-encoding` attribute
   in your repository, then it is strongly recommended to ensure that all
   clients working with the repository support it.
-
-  For example, Microsoft Visual Studio resources files (`*.rc`) or
-  PowerShell script files (`*.ps1`) are sometimes encoded in UTF-16.
-  If you declare `*.ps1` as files as UTF-16 and you add `foo.ps1` with
-  a `working-tree-encoding` enabled Git client, then `foo.ps1` will be
-  stored as UTF-8 internally. A client without `working-tree-encoding`
-  support will checkout `foo.ps1` as UTF-8 encoded file. This will
-  typically cause trouble for the users of this file.
-
-  If a Git client, that does not support the `working-tree-encoding`
-  attribute, adds a new file `bar.ps1`, then `bar.ps1` will be
-  stored "as-is" internally (in this example probably as UTF-16).
-  A client with `working-tree-encoding` support will interpret the
-  internal contents as UTF-8 and try to convert it to UTF-16 on checkout.
-  That operation will fail and cause an error.
++
+For example, Microsoft Visual Studio resources files (`*.rc`) or
+PowerShell script files (`*.ps1`) are sometimes encoded in UTF-16.
+If you declare `*.ps1` as files as UTF-16 and you add `foo.ps1` with
+a `working-tree-encoding` enabled Git client, then `foo.ps1` will be
+stored as UTF-8 internally. A client without `working-tree-encoding`
+support will checkout `foo.ps1` as UTF-8 encoded file. This will
+typically cause trouble for the users of this file.
++
+If a Git client, that does not support the `working-tree-encoding`
+attribute, adds a new file `bar.ps1`, then `bar.ps1` will be
+stored "as-is" internally (in this example probably as UTF-16).
+A client with `working-tree-encoding` support will interpret the
+internal contents as UTF-8 and try to convert it to UTF-16 on checkout.
+That operation will fail and cause an error.
 
 - Reencoding content to non-UTF encodings can cause errors as the
   conversion might not be UTF-8 round trip safe. If you suspect your
index 4d63def2069a8358afa05154c18653175dda6703..312b6f92592258b88efcaed36392df998bfaba0a 100644 (file)
@@ -67,7 +67,8 @@ submodule.<name>.fetchRecurseSubmodules::
 submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
        a submodule as modified. The following values are supported:
-
++
+--
        all;; The submodule will never be considered modified (but will
            nonetheless show up in the output of status and commit when it has
            been staged).
@@ -84,12 +85,14 @@ submodule.<name>.ignore::
            differences, and modifications to tracked and untracked files are
            shown. This is the default option.
 
-       If this option is also present in the submodules entry in .git/config
-       of the superproject, the setting there will override the one found in
-       .gitmodules.
-       Both settings can be overridden on the command line by using the
-       "--ignore-submodule" option. The 'git submodule' commands are not
-       affected by this setting.
+If this option is also present in the submodules entry in .git/config
+of the superproject, the setting there will override the one found in
+.gitmodules.
+
+Both settings can be overridden on the command line by using the
+"--ignore-submodule" option. The 'git submodule' commands are not
+affected by this setting.
+--
 
 submodule.<name>.shallow::
        When set to true, a clone of this submodule will be performed as a
index 504c5f1a885dfb20c37a9f87bdbdbd452cd216bd..57999e9f36686dc24e8316e91869f79b65a54960 100644 (file)
@@ -169,11 +169,15 @@ ACTIVE SUBMODULES
 
 A submodule is considered active,
 
-  (a) if `submodule.<name>.active` is set to `true`
-     or
-  (b) if the submodule's path matches the pathspec in `submodule.active`
-     or
-  (c) if `submodule.<name>.url` is set.
+  a. if `submodule.<name>.active` is set to `true`
++
+or
+
+  b. if the submodule's path matches the pathspec in `submodule.active`
++
+or
+
+  c. if `submodule.<name>.url` is set.
 
 and these are evaluated in this order.
 
index 9c8982ec98744852e69e96455ec6b2bbce093556..c0a326e3883c321f6a5b0c545d9aee9cdd1a5d8a 100644 (file)
@@ -19,10 +19,12 @@ end of a line is ignored.  See *perlsyn*(1) for details.
 
 An example:
 
-    # gitweb configuration file for http://git.example.org
-    #
-    our $projectroot = "/srv/git"; # FHS recommendation
-    our $site_name = 'Example.org >> Repos';
+------------------------------------------------
+# gitweb configuration file for http://git.example.org
+#
+our $projectroot = "/srv/git"; # FHS recommendation
+our $site_name = 'Example.org >> Repos';
+------------------------------------------------
 
 
 The configuration file is used to override the default settings that
@@ -357,6 +359,7 @@ $home_link_str::
 +
 For example, the following setting produces a breadcrumb trail like
 "home / dev / projects / ..." where "projects" is the home link.
++
 ----------------------------------------------------------------------------
     our @extra_breadcrumbs = (
       [ 'home' => 'https://www.example.org/' ],
@@ -901,14 +904,16 @@ To enable blame, pickaxe search, and snapshot support (allowing "tar.gz" and
 "zip" snapshots), while allowing individual projects to turn them off, put
 the following in your GITWEB_CONFIG file:
 
-       $feature{'blame'}{'default'} = [1];
-       $feature{'blame'}{'override'} = 1;
+--------------------------------------------------------------------------------
+$feature{'blame'}{'default'} = [1];
+$feature{'blame'}{'override'} = 1;
 
-       $feature{'pickaxe'}{'default'} = [1];
-       $feature{'pickaxe'}{'override'} = 1;
+$feature{'pickaxe'}{'default'} = [1];
+$feature{'pickaxe'}{'override'} = 1;
 
-       $feature{'snapshot'}{'default'} = ['zip', 'tgz'];
-       $feature{'snapshot'}{'override'} = 1;
+$feature{'snapshot'}{'default'} = ['zip', 'tgz'];
+$feature{'snapshot'}{'override'} = 1;
+--------------------------------------------------------------------------------
 
 If you allow overriding for the snapshot feature, you can specify which
 snapshot formats are globally disabled. You can also add any command-line
index a5193b1e5c45e3d9a78de7604f104a8a80c3bdd8..89821ec74fe1d71764bcb320d1ac1021bd06201f 100644 (file)
@@ -80,7 +80,7 @@ case "$1" in
       info "The branch '$1' is new..."
     else
       # updating -- make sure it is a fast-forward
-      mb=$(git-merge-base "$2" "$3")
+      mb=$(git merge-base "$2" "$3")
       case "$mb,$2" in
         "$2,$mb") info "Update is fast-forward" ;;
        *)        noff=y; info "This is not a fast-forward update.";;
index 1ae8d1214ec0e8db1979a7770cf7602403358636..a499a94ac2289ac6664035d339e37914748757d2 100644 (file)
@@ -33,7 +33,7 @@ Here is the command sequence you need:
 
 ----------------
 $ git remote add -f Bproject /path/to/B <1>
-$ git merge -s ours --no-commit Bproject/master <2>
+$ git merge -s ours --no-commit --allow-unrelated-histories Bproject/master <2>
 $ git read-tree --prefix=dir-B/ -u Bproject/master <3>
 $ git commit -m "Merge B project as our subdirectory" <4>
 
index 662c2713cab4b9762dded702897d9e92ee0a0549..a7f4ea90c11ff6092e77057bd5bee241499cf40f 100644 (file)
@@ -63,6 +63,12 @@ merge.tool::
        Any other value is treated as a custom merge tool and requires
        that a corresponding mergetool.<tool>.cmd variable is defined.
 
+merge.guitool::
+       Controls which merge tool is used by linkgit:git-mergetool[1] when the
+       -g/--gui flag is specified. The list below shows the valid built-in values.
+       Any other value is treated as a custom merge tool and requires that a
+       corresponding mergetool.<guitool>.cmd variable is defined.
+
 include::mergetools-merge.txt[]
 
 merge.verbosity::
index 6109ef09aa2eeba564023cced75728ffaa1c4f96..417b638cd803e6cf3b106f3bd7333ba4f72d800a 100644 (file)
@@ -153,6 +153,9 @@ endif::git-rev-list[]
   and "N" for no signature
 - '%GS': show the name of the signer for a signed commit
 - '%GK': show the key used to sign a signed commit
+- '%GF': show the fingerprint of the key used to sign a signed commit
+- '%GP': show the fingerprint of the primary key whose subkey was used
+  to sign a signed commit
 - '%gD': reflog selector, e.g., `refs/stash@{1}` or
   `refs/stash@{2 minutes ago`}; the format follows the rules described
   for the `-g` option. The portion before the `@` is the refname as
index 7b273635de2b5bf3e3ba6ade8bcca3068a216395..5f1672913b8ab19f6b2cf20d5fa79cfe6b636356 100644 (file)
@@ -731,6 +731,11 @@ the requested refs.
 +
 The form '--filter=sparse:path=<path>' similarly uses a sparse-checkout
 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.
 
 --no-filter::
        Turn off any previous `--filter=` argument.
index 8b001de0db3628d4f02c2890d5614e067e701ed4..30fc0e9c93b2562b87abb107acf5ad2ae1c7fea0 100644 (file)
@@ -18,8 +18,8 @@ Calling sequence
 ----------------
 
 * Prepare `struct diff_options` to record the set of diff options, and
-  then call `diff_setup()` to initialize this structure.  This sets up
-  the vanilla default.
+  then call `repo_diff_setup()` to initialize this structure.  This
+  sets up the vanilla default.
 
 * Fill in the options structure to specify desired output format, rename
   detection, etc.  `diff_opt_parse()` can be used to parse options given
index 55b878ade81e2b3b265032e281f82ab86016d5ae..03f9ea6ac4bad96c9714cd7948e70959ea682d30 100644 (file)
@@ -15,9 +15,9 @@ revision list.
 Functions
 ---------
 
-`init_revisions`::
+`repo_init_revisions`::
 
-       Initialize a rev_info structure with default values. The second
+       Initialize a rev_info structure with default values. The third
        parameter may be NULL or can be prefix path, and then the `.prefix`
        variable will be set to it. This is typically the first function you
        want to call when you want to deal with a revision list. After calling
index db3572626beba6fc04f169b4ccccca0084d7326b..7c4d67aa6a7f04f147cd89335ee0125d6912061d 100644 (file)
@@ -314,3 +314,44 @@ The remaining data of each directory block is grouped by type:
 
   - An ewah bitmap, the n-th bit indicates whether the n-th index entry
     is not CE_FSMONITOR_VALID.
+
+== End of Index Entry
+
+  The End of Index Entry (EOIE) is used to locate the end of the variable
+  length index entries and the begining of the extensions. Code can take
+  advantage of this to quickly locate the index extensions without having
+  to parse through all of the index entries.
+
+  Because it must be able to be loaded before the variable length cache
+  entries and other index extensions, this extension must be written last.
+  The signature for this extension is { 'E', 'O', 'I', 'E' }.
+
+  The extension consists of:
+
+  - 32-bit offset to the end of the index entries
+
+  - 160-bit SHA-1 over the extension types and their sizes (but not
+       their contents).  E.g. if we have "TREE" extension that is N-bytes
+       long, "REUC" extension that is M-bytes long, followed by "EOIE",
+       then the hash would be:
+
+       SHA-1("TREE" + <binary representation of N> +
+               "REUC" + <binary representation of M>)
+
+== Index Entry Offset Table
+
+  The Index Entry Offset Table (IEOT) is used to help address the CPU
+  cost of loading the index by enabling multi-threading the process of
+  converting cache entries from the on-disk format to the in-memory format.
+  The signature for this extension is { 'I', 'E', 'O', 'T' }.
+
+  The extension consists of:
+
+  - 32-bit version (currently 1)
+
+  - A number of index offset entries each consisting of:
+
+    - 32-bit offset from the begining of the file to the first cache entry
+       in this block of entries.
+
+    - 32-bit count of cache entries in this block
index 5bf1af369ec46421c68b9194bbb9938e2ba2933f..bbfbb4292d49e99704d75a093a97d432c537cd8d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -400,7 +400,7 @@ all::
 # (defaults to "man") if you want to have a different default when
 # "git help" is called without a parameter specifying the format.
 #
-# Define TEST_GIT_INDEX_VERSION to 2, 3 or 4 to run the test suite
+# Define GIT_TEST_INDEX_VERSION to 2, 3 or 4 to run the test suite
 # with a different indexfile format version.  If it isn't set the index
 # file format used is index-v[23].
 #
@@ -590,6 +590,8 @@ XDIFF_OBJS =
 VCSSVN_OBJS =
 GENERATED_H =
 EXTRA_CPPFLAGS =
+FUZZ_OBJS =
+FUZZ_PROGRAMS =
 LIB_OBJS =
 PROGRAM_OBJS =
 PROGRAMS =
@@ -614,7 +616,7 @@ SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-rebase.sh
+SCRIPT_SH += git-legacy-rebase.sh
 SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
@@ -624,7 +626,7 @@ SCRIPT_SH += git-web--browse.sh
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-rebase--am
-SCRIPT_LIB += git-rebase--interactive
+SCRIPT_LIB += git-rebase--common
 SCRIPT_LIB += git-rebase--preserve-merges
 SCRIPT_LIB += git-rebase--merge
 SCRIPT_LIB += git-sh-setup
@@ -682,6 +684,14 @@ SCRIPTS = $(SCRIPT_SH_INS) \
 
 ETAGS_TARGET = TAGS
 
+FUZZ_OBJS += fuzz-pack-headers.o
+FUZZ_OBJS += fuzz-pack-idx.o
+
+# Always build fuzz objects even if not testing, to prevent bit-rot.
+all:: $(FUZZ_OBJS)
+
+FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS))
+
 # Empty...
 EXTRA_PROGRAMS =
 
@@ -944,6 +954,7 @@ LIB_OBJS += quote.o
 LIB_OBJS += range-diff.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
+LIB_OBJS += rebase-interactive.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
@@ -1083,7 +1094,8 @@ BUILTIN_OBJS += builtin/pull.o
 BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/range-diff.o
 BUILTIN_OBJS += builtin/read-tree.o
-BUILTIN_OBJS += builtin/rebase--helper.o
+BUILTIN_OBJS += builtin/rebase.o
+BUILTIN_OBJS += builtin/rebase--interactive.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
 BUILTIN_OBJS += builtin/remote.o
@@ -2253,6 +2265,7 @@ TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST
 OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
        $(XDIFF_OBJS) \
        $(VCSSVN_OBJS) \
+       $(FUZZ_OBJS) \
        common-main.o \
        git.o
 ifndef NO_CURL
@@ -2423,7 +2436,6 @@ XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
-LOCALIZED_SH += git-rebase--interactive.sh
 LOCALIZED_SH += git-rebase--preserve-merges.sh
 LOCALIZED_SH += git-sh-setup.sh
 LOCALIZED_PERL = $(SCRIPT_PERL)
@@ -2610,8 +2622,8 @@ endif
 ifdef GIT_INTEROP_MAKE_OPTS
        @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
 endif
-ifdef TEST_GIT_INDEX_VERSION
-       @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
+ifdef GIT_TEST_INDEX_VERSION
+       @echo GIT_TEST_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_INDEX_VERSION)))'\' >>$@+
 endif
        @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
 
@@ -2951,6 +2963,7 @@ clean: profile-clean coverage-clean cocciclean
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
+       $(RM) $(FUZZ_PROGRAMS)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
        $(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope*
@@ -3075,3 +3088,24 @@ cover_db: coverage-report
 cover_db_html: cover_db
        cover -report html -outputdir cover_db_html cover_db
 
+
+### Fuzz testing
+#
+# Building fuzz targets generally requires a special set of compiler flags that
+# are not necessarily appropriate for general builds, and that vary greatly
+# depending on the compiler version used.
+#
+# An example command to build against libFuzzer from LLVM 4.0.0:
+#
+# make CC=clang CXX=clang++ \
+#      CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
+#      LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
+#      fuzz-all
+#
+.PHONY: fuzz-all
+
+$(FUZZ_PROGRAMS): all
+       $(QUIET_LINK)$(CXX) $(CFLAGS) $(LIB_OBJS) $(BUILTIN_OBJS) \
+               $(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@
+
+fuzz-all: $(FUZZ_PROGRAMS)
diff --git a/alias.h b/alias.h
index 79933f2457ce265e2d141459308c8407488cca69..aef4843bb7821e87dcbe017ddfff64b8d6681fca 100644 (file)
--- a/alias.h
+++ b/alias.h
@@ -1,5 +1,5 @@
-#ifndef __ALIAS_H__
-#define __ALIAS_H__
+#ifndef ALIAS_H
+#define ALIAS_H
 
 struct string_list;
 
diff --git a/apply.c b/apply.c
index e485fbc6bc11efc6daefe78cbe949a65560ff4fa..073d5f04512ac6febc2949040f53ef273433f3c0 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -223,8 +223,8 @@ struct patch {
        struct fragment *fragments;
        char *result;
        size_t resultsize;
-       char old_sha1_prefix[41];
-       char new_sha1_prefix[41];
+       char old_oid_prefix[GIT_MAX_HEXSZ + 1];
+       char new_oid_prefix[GIT_MAX_HEXSZ + 1];
        struct patch *next;
 
        /* three-way fallback result */
@@ -1093,13 +1093,14 @@ static int gitdiff_index(struct apply_state *state,
         */
        const char *ptr, *eol;
        int len;
+       const unsigned hexsz = the_hash_algo->hexsz;
 
        ptr = strchr(line, '.');
-       if (!ptr || ptr[1] != '.' || 40 < ptr - line)
+       if (!ptr || ptr[1] != '.' || hexsz < ptr - line)
                return 0;
        len = ptr - line;
-       memcpy(patch->old_sha1_prefix, line, len);
-       patch->old_sha1_prefix[len] = 0;
+       memcpy(patch->old_oid_prefix, line, len);
+       patch->old_oid_prefix[len] = 0;
 
        line = ptr + 2;
        ptr = strchr(line, ' ');
@@ -1109,10 +1110,10 @@ static int gitdiff_index(struct apply_state *state,
                ptr = eol;
        len = ptr - line;
 
-       if (40 < len)
+       if (hexsz < len)
                return 0;
-       memcpy(patch->new_sha1_prefix, line, len);
-       patch->new_sha1_prefix[len] = 0;
+       memcpy(patch->new_oid_prefix, line, len);
+       patch->new_oid_prefix[len] = 0;
        if (*ptr == ' ')
                return gitdiff_oldmode(state, ptr + 1, patch);
        return 0;
@@ -2131,10 +2132,12 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si
 
        if (!use_patch(state, patch))
                patch->ws_rule = 0;
+       else if (patch->new_name)
+               patch->ws_rule = whitespace_rule(state->repo->index,
+                                                patch->new_name);
        else
-               patch->ws_rule = whitespace_rule(patch->new_name
-                                                ? patch->new_name
-                                                : patch->old_name);
+               patch->ws_rule = whitespace_rule(state->repo->index,
+                                                patch->old_name);
 
        patchsize = parse_single_patch(state,
                                       buffer + offset + hdrsize,
@@ -2204,7 +2207,7 @@ static void reverse_patches(struct patch *p)
                SWAP(p->new_mode, p->old_mode);
                SWAP(p->is_new, p->is_delete);
                SWAP(p->lines_added, p->lines_deleted);
-               SWAP(p->old_sha1_prefix, p->new_sha1_prefix);
+               SWAP(p->old_oid_prefix, p->new_oid_prefix);
 
                for (; frag; frag = frag->next) {
                        SWAP(frag->newpos, frag->oldpos);
@@ -3142,15 +3145,16 @@ static int apply_binary(struct apply_state *state,
 {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        struct object_id oid;
+       const unsigned hexsz = the_hash_algo->hexsz;
 
        /*
         * For safety, we require patch index line to contain
-        * full 40-byte textual SHA1 for old and new, at least for now.
+        * full hex textual object ID for old and new, at least for now.
         */
-       if (strlen(patch->old_sha1_prefix) != 40 ||
-           strlen(patch->new_sha1_prefix) != 40 ||
-           get_oid_hex(patch->old_sha1_prefix, &oid) ||
-           get_oid_hex(patch->new_sha1_prefix, &oid))
+       if (strlen(patch->old_oid_prefix) != hexsz ||
+           strlen(patch->new_oid_prefix) != hexsz ||
+           get_oid_hex(patch->old_oid_prefix, &oid) ||
+           get_oid_hex(patch->new_oid_prefix, &oid))
                return error(_("cannot apply binary patch to '%s' "
                               "without full index line"), name);
 
@@ -3160,7 +3164,7 @@ static int apply_binary(struct apply_state *state,
                 * applies to.
                 */
                hash_object_file(img->buf, img->len, blob_type, &oid);
-               if (strcmp(oid_to_hex(&oid), patch->old_sha1_prefix))
+               if (strcmp(oid_to_hex(&oid), patch->old_oid_prefix))
                        return error(_("the patch applies to '%s' (%s), "
                                       "which does not match the "
                                       "current contents."),
@@ -3173,7 +3177,7 @@ static int apply_binary(struct apply_state *state,
                                       "'%s' but it is not empty"), name);
        }
 
-       get_oid_hex(patch->new_sha1_prefix, &oid);
+       get_oid_hex(patch->new_oid_prefix, &oid);
        if (is_null_oid(&oid)) {
                clear_image(img);
                return 0; /* deletion patch */
@@ -3189,7 +3193,7 @@ static int apply_binary(struct apply_state *state,
                if (!result)
                        return error(_("the necessary postimage %s for "
                                       "'%s' cannot be read"),
-                                    patch->new_sha1_prefix, name);
+                                    patch->new_oid_prefix, name);
                clear_image(img);
                img->buf = result;
                img->len = size;
@@ -3205,9 +3209,9 @@ static int apply_binary(struct apply_state *state,
 
                /* verify that the result matches */
                hash_object_file(img->buf, img->len, blob_type, &oid);
-               if (strcmp(oid_to_hex(&oid), patch->new_sha1_prefix))
+               if (strcmp(oid_to_hex(&oid), patch->new_oid_prefix))
                        return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
-                               name, patch->new_sha1_prefix, oid_to_hex(&oid));
+                               name, patch->new_oid_prefix, oid_to_hex(&oid));
        }
 
        return 0;
@@ -3467,7 +3471,8 @@ static int load_preimage(struct apply_state *state,
        return 0;
 }
 
-static int three_way_merge(struct image *image,
+static int three_way_merge(struct apply_state *state,
+                          struct image *image,
                           char *path,
                           const struct object_id *base,
                           const struct object_id *ours,
@@ -3483,7 +3488,9 @@ static int three_way_merge(struct image *image,
        status = ll_merge(&result, path,
                          &base_file, "base",
                          &our_file, "ours",
-                         &their_file, "theirs", NULL);
+                         &their_file, "theirs",
+                         state->repo->index,
+                         NULL);
        free(base_file.ptr);
        free(our_file.ptr);
        free(their_file.ptr);
@@ -3563,7 +3570,7 @@ static int try_threeway(struct apply_state *state,
        /* Preimage the patch was prepared for */
        if (patch->is_new)
                write_object_file("", 0, blob_type, &pre_oid);
-       else if (get_oid(patch->old_sha1_prefix, &pre_oid) ||
+       else if (get_oid(patch->old_oid_prefix, &pre_oid) ||
                 read_blob_object(&buf, &pre_oid, patch->old_mode))
                return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
 
@@ -3595,7 +3602,7 @@ static int try_threeway(struct apply_state *state,
        clear_image(&tmp_image);
 
        /* in-core three-way merge between post and our using pre as base */
-       status = three_way_merge(image, patch->new_name,
+       status = three_way_merge(state, image, patch->new_name,
                                 &pre_oid, &our_oid, &post_oid);
        if (status < 0) {
                if (state->apply_verbosity > verbosity_silent)
@@ -4055,13 +4062,13 @@ static int preimage_oid_in_gitlink_patch(struct patch *p, struct object_id *oid)
            starts_with(++preimage, heading) &&
            /* does it record full SHA-1? */
            !get_oid_hex(preimage + sizeof(heading) - 1, oid) &&
-           preimage[sizeof(heading) + GIT_SHA1_HEXSZ - 1] == '\n' &&
+           preimage[sizeof(heading) + the_hash_algo->hexsz - 1] == '\n' &&
            /* does the abbreviated name on the index line agree with it? */
-           starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
+           starts_with(preimage + sizeof(heading) - 1, p->old_oid_prefix))
                return 0; /* it all looks fine */
 
        /* we may have full object name on the index line */
-       return get_oid_hex(p->old_sha1_prefix, oid);
+       return get_oid_hex(p->old_oid_prefix, oid);
 }
 
 /* Build an index that contains just the files needed for a 3way merge */
@@ -4090,7 +4097,7 @@ static int build_fake_ancestor(struct apply_state *state, struct patch *list)
                        else
                                return error(_("sha1 information is lacking or "
                                               "useless for submodule %s"), name);
-               } else if (!get_oid_blob(patch->old_sha1_prefix, &oid)) {
+               } else if (!get_oid_blob(patch->old_oid_prefix, &oid)) {
                        ; /* ok */
                } else if (!patch->lines_added && !patch->lines_deleted) {
                        /* mode-only change: update the current */
@@ -4627,7 +4634,7 @@ static int write_out_results(struct apply_state *state, struct patch *list)
                }
                string_list_clear(&cpath, 0);
 
-               rerere(0);
+               repo_rerere(state->repo, 0);
        }
 
        return errs;
index 5a62351ab1a46f6a0827a7d41e794c00a4839edc..155ee4a779a1c2cd0cb2a7a8eea8c7ba8ba8502a 100644 (file)
@@ -264,9 +264,10 @@ static int has_only_ascii(const char *s)
        }
 }
 
-static int entry_is_binary(const char *path, const void *buffer, size_t size)
+static int entry_is_binary(struct index_state *istate, const char *path,
+                          const void *buffer, size_t size)
 {
-       struct userdiff_driver *driver = userdiff_find_by_path(path);
+       struct userdiff_driver *driver = userdiff_find_by_path(istate, path);
        if (!driver)
                driver = userdiff_find_by_name("default");
        if (driver->binary != -1)
@@ -352,7 +353,8 @@ static int write_zip_entry(struct archiver_args *args,
                                return error(_("cannot read %s"),
                                             oid_to_hex(oid));
                        crc = crc32(crc, buffer, size);
-                       is_binary = entry_is_binary(path_without_prefix,
+                       is_binary = entry_is_binary(args->repo->index,
+                                                   path_without_prefix,
                                                    buffer, size);
                        out = buffer;
                }
@@ -428,7 +430,8 @@ static int write_zip_entry(struct archiver_args *args,
                                break;
                        crc = crc32(crc, buf, readlen);
                        if (is_binary == -1)
-                               is_binary = entry_is_binary(path_without_prefix,
+                               is_binary = entry_is_binary(args->repo->index,
+                                                           path_without_prefix,
                                                            buf, readlen);
                        write_or_die(1, buf, readlen);
                }
@@ -460,7 +463,8 @@ static int write_zip_entry(struct archiver_args *args,
                                break;
                        crc = crc32(crc, buf, readlen);
                        if (is_binary == -1)
-                               is_binary = entry_is_binary(path_without_prefix,
+                               is_binary = entry_is_binary(args->repo->index,
+                                                           path_without_prefix,
                                                            buf, readlen);
 
                        zstream.next_in = buf;
index c1870105eb453980ce421f937e9615d1d9dcd3b3..fd556c28e420732bbe625b68663f9099a5316f96 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -29,6 +29,12 @@ void register_archiver(struct archiver *ar)
        archivers[nr_archivers++] = ar;
 }
 
+void init_archivers(void)
+{
+       init_tar_archiver();
+       init_zip_archiver();
+}
+
 static void format_subst(const struct commit *commit,
                          const char *src, size_t len,
                          struct strbuf *buf)
@@ -392,7 +398,7 @@ static void parse_treeish_arg(const char **argv,
        if (get_oid(name, &oid))
                die("Not a valid object name");
 
-       commit = lookup_commit_reference_gently(the_repository, &oid, 1);
+       commit = lookup_commit_reference_gently(ar_args->repo, &oid, 1);
        if (commit) {
                commit_sha1 = commit->object.oid.hash;
                archive_time = commit->date;
@@ -531,9 +537,6 @@ int write_archive(int argc, const char **argv, const char *prefix,
        git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable);
        git_config(git_default_config, NULL);
 
-       init_tar_archiver();
-       init_zip_archiver();
-
        args.repo = repo;
        argc = parse_archive_args(argc, argv, &ar, &args, name_hint, remote);
        if (!startup_info->have_repository) {
index d4f97a00f541c66b694ff00340ead218752bf37e..21ac010699f034e7c8278a22d5387adf74b83ec8 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -43,6 +43,7 @@ extern void register_archiver(struct archiver *);
 
 extern void init_tar_archiver(void);
 extern void init_zip_archiver(void);
+extern void init_archivers(void);
 
 typedef int (*write_archive_entry_fn_t)(struct archiver_args *args,
                                        const struct object_id *oid,
index e8b17cf7e1d8d2bf7895e2fa33d0cf0cbc47ae63..487675c67249a3164294aae71c1c2a650f3c4947 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -633,7 +633,7 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
        struct argv_array rev_argv = ARGV_ARRAY_INIT;
        int i;
 
-       init_revisions(revs, prefix);
+       repo_init_revisions(the_repository, revs, prefix);
        revs->abbrev = 0;
        revs->commit_format = CMIT_FMT_UNSPECIFIED;
 
@@ -890,7 +890,7 @@ static void show_diff_tree(const char *prefix, struct commit *commit)
        struct rev_info opt;
 
        /* diff-tree init */
-       init_revisions(&opt, prefix);
+       repo_init_revisions(the_repository, &opt, prefix);
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        opt.abbrev = 0;
        opt.diff = 1;
diff --git a/blame.c b/blame.c
index d5f7b7237c4e9f9dcb6ad37504a98a0fcff66f80..d84c937780801f51b8eb98b7e367c8017701ba5c 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -90,7 +90,7 @@ static struct blame_origin *get_origin(struct commit *commit, const char *path)
 
 
 
-static void verify_working_tree_path(struct repository *repo,
+static void verify_working_tree_path(struct repository *r,
                                     struct commit *work_tree, const char *path)
 {
        struct commit_list *parents;
@@ -102,15 +102,15 @@ static void verify_working_tree_path(struct repository *repo,
                unsigned mode;
 
                if (!get_tree_entry(commit_oid, path, &blob_oid, &mode) &&
-                   oid_object_info(repo, &blob_oid, NULL) == OBJ_BLOB)
+                   oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)
                        return;
        }
 
-       pos = index_name_pos(repo->index, path, strlen(path));
+       pos = index_name_pos(r->index, path, strlen(path));
        if (pos >= 0)
                ; /* path is in the index */
-       else if (-1 - pos < repo->index->cache_nr &&
-                !strcmp(repo->index->cache[-1 - pos]->name, path))
+       else if (-1 - pos < r->index->cache_nr &&
+                !strcmp(r->index->cache[-1 - pos]->name, path))
                ; /* path is in the index, unmerged */
        else
                die("no such path '%s' in HEAD", path);
@@ -166,7 +166,7 @@ static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
  * Prepare a dummy commit that represents the work tree (or staged) item.
  * Note that annotating work tree item never works in the reverse.
  */
-static struct commit *fake_working_tree_commit(struct repository *repo,
+static struct commit *fake_working_tree_commit(struct repository *r,
                                               struct diff_options *opt,
                                               const char *path,
                                               const char *contents_from)
@@ -183,7 +183,7 @@ static struct commit *fake_working_tree_commit(struct repository *repo,
        unsigned mode;
        struct strbuf msg = STRBUF_INIT;
 
-       read_index(repo->index);
+       read_index(r->index);
        time(&now);
        commit = alloc_commit_node(the_repository);
        commit->object.parsed = 1;
@@ -195,7 +195,7 @@ static struct commit *fake_working_tree_commit(struct repository *repo,
 
        parent_tail = append_parent(parent_tail, &head_oid);
        append_merge_parents(parent_tail);
-       verify_working_tree_path(repo, commit, path);
+       verify_working_tree_path(r, commit, path);
 
        origin = make_origin(commit, path);
 
@@ -234,7 +234,7 @@ static struct commit *fake_working_tree_commit(struct repository *repo,
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (opt->flags.allow_textconv &&
-                           textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
+                           textconv_object(r, read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
                                strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
@@ -253,7 +253,7 @@ static struct commit *fake_working_tree_commit(struct repository *repo,
                if (strbuf_read(&buf, 0, 0) < 0)
                        die_errno("failed to read from stdin");
        }
-       convert_to_git(repo->index, path, buf.buf, buf.len, &buf, 0);
+       convert_to_git(r->index, path, buf.buf, buf.len, &buf, 0);
        origin->file.ptr = buf.buf;
        origin->file.size = buf.len;
        pretend_object_file(buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid);
@@ -264,28 +264,28 @@ static struct commit *fake_working_tree_commit(struct repository *repo,
         * bits; we are not going to write this index out -- we just
         * want to run "diff-index --cached".
         */
-       discard_index(repo->index);
-       read_index(repo->index);
+       discard_index(r->index);
+       read_index(r->index);
 
        len = strlen(path);
        if (!mode) {
-               int pos = index_name_pos(repo->index, path, len);
+               int pos = index_name_pos(r->index, path, len);
                if (0 <= pos)
-                       mode = repo->index->cache[pos]->ce_mode;
+                       mode = r->index->cache[pos]->ce_mode;
                else
                        /* Let's not bother reading from HEAD tree */
                        mode = S_IFREG | 0644;
        }
-       ce = make_empty_cache_entry(repo->index, len);
+       ce = make_empty_cache_entry(r->index, len);
        oidcpy(&ce->oid, &origin->blob_oid);
        memcpy(ce->name, path, len);
        ce->ce_flags = create_ce_flags(0);
        ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
-       add_index_entry(repo->index, ce,
+       add_index_entry(r->index, ce,
                        ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 
-       cache_tree_invalidate_path(repo->index, path);
+       cache_tree_invalidate_path(r->index, path);
 
        return commit;
 }
@@ -318,7 +318,8 @@ static void fill_origin_blob(struct diff_options *opt,
 
                (*num_read_blob)++;
                if (opt->flags.allow_textconv &&
-                   textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
+                   textconv_object(opt->repo, o->path, o->mode,
+                                   &o->blob_oid, 1, &file->ptr, &file_size))
                        ;
                else
                        file->ptr = read_object_file(&o->blob_oid, &type,
@@ -520,14 +521,14 @@ static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porig
  *
  * This also fills origin->mode for corresponding tree path.
  */
-static int fill_blob_sha1_and_mode(struct repository *repo,
+static int fill_blob_sha1_and_mode(struct repository *r,
                                   struct blame_origin *origin)
 {
        if (!is_null_oid(&origin->blob_oid))
                return 0;
        if (get_tree_entry(&origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))
                goto error_out;
-       if (oid_object_info(repo, &origin->blob_oid, NULL) != OBJ_BLOB)
+       if (oid_object_info(r, &origin->blob_oid, NULL) != OBJ_BLOB)
                goto error_out;
        return 0;
  error_out:
@@ -540,8 +541,9 @@ static int fill_blob_sha1_and_mode(struct repository *repo,
  * We have an origin -- check if the same path exists in the
  * parent and return an origin structure to represent it.
  */
-static struct blame_origin *find_origin(struct commit *parent,
-                                 struct blame_origin *origin)
+static struct blame_origin *find_origin(struct repository *r,
+                                       struct commit *parent,
+                                       struct blame_origin *origin)
 {
        struct blame_origin *porigin;
        struct diff_options diff_opts;
@@ -561,7 +563,7 @@ static struct blame_origin *find_origin(struct commit *parent,
         * and origin first.  Most of the time they are the
         * same and diff-tree is fairly efficient about this.
         */
-       diff_setup(&diff_opts);
+       repo_diff_setup(r, &diff_opts);
        diff_opts.flags.recursive = 1;
        diff_opts.detect_rename = 0;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -628,14 +630,15 @@ static struct blame_origin *find_origin(struct commit *parent,
  * We have an origin -- find the path that corresponds to it in its
  * parent and return an origin structure to represent it.
  */
-static struct blame_origin *find_rename(struct commit *parent,
-                                 struct blame_origin *origin)
+static struct blame_origin *find_rename(struct repository *r,
+                                       struct commit *parent,
+                                       struct blame_origin *origin)
 {
        struct blame_origin *porigin = NULL;
        struct diff_options diff_opts;
        int i;
 
-       diff_setup(&diff_opts);
+       repo_diff_setup(r, &diff_opts);
        diff_opts.flags.recursive = 1;
        diff_opts.detect_rename = DIFF_DETECT_RENAME;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -1259,7 +1262,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb,
        if (!unblamed)
                return; /* nothing remains for this target */
 
-       diff_setup(&diff_opts);
+       repo_diff_setup(sb->repo, &diff_opts);
        diff_opts.flags.recursive = 1;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 
@@ -1441,7 +1444,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,
         * common cases, then we look for renames in the second pass.
         */
        for (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) {
-               struct blame_origin *(*find)(struct commit *, struct blame_origin *);
+               struct blame_origin *(*find)(struct repository *, struct commit *, struct blame_origin *);
                find = pass ? find_rename : find_origin;
 
                for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
@@ -1454,7 +1457,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,
                                continue;
                        if (parse_commit(p))
                                continue;
-                       porigin = find(p, origin);
+                       porigin = find(sb->repo, p, origin);
                        if (!porigin)
                                continue;
                        if (oideq(&porigin->blob_oid, &origin->blob_oid)) {
@@ -1857,7 +1860,7 @@ void setup_scoreboard(struct blame_scoreboard *sb,
                        die(_("no such path %s in %s"), path, final_commit_name);
 
                if (sb->revs->diffopt.flags.allow_textconv &&
-                   textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
+                   textconv_object(sb->repo, path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
                                    &sb->final_buf_size))
                        ;
                else
index 962f0489ab212cc613d98ac2577f3999278d099b..6538932e99a72f1be97c41806ca6e394f18f9a0c 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -204,7 +204,8 @@ extern int cmd_pull(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_range_diff(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_rebase__helper(int argc, const char **argv, const char *prefix);
+extern int cmd_rebase(int argc, const char **argv, const char *prefix);
+extern int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
index 0b64bcdebe0f4581953adccf8addf1c3fd1eb61e..ad49806ebf92208e25a69ac9bff3f0e0a3b96570 100644 (file)
@@ -110,7 +110,7 @@ int add_files_to_cache(const char *prefix,
        memset(&data, 0, sizeof(data));
        data.flags = flags;
 
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
        if (pathspec)
                copy_pathspec(&rev.prune_data, pathspec);
@@ -232,7 +232,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
        if (read_cache() < 0)
                die(_("Could not read the index"));
 
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        rev.diffopt.context = 7;
 
        argc = setup_revisions(argc, argv, &rev, NULL);
index 5e643e2a3e3d70d9c674d3edab2eb1f2f08f3392..3ee9a9d2a92aaa0e9716719640deaec7e260c811 100644 (file)
@@ -1376,7 +1376,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
        FILE *fp;
 
        fp = xfopen(am_path(state, "patch"), "w");
-       init_revisions(&rev_info, NULL);
+       repo_init_revisions(the_repository, &rev_info, NULL);
        rev_info.diff = 1;
        rev_info.abbrev = 0;
        rev_info.disable_stdin = 1;
@@ -1411,7 +1411,7 @@ static void write_index_patch(const struct am_state *state)
                                   the_repository->hash_algo->empty_tree);
 
        fp = xfopen(am_path(state, "patch"), "w");
-       init_revisions(&rev_info, NULL);
+       repo_init_revisions(the_repository, &rev_info, NULL);
        rev_info.diff = 1;
        rev_info.disable_stdin = 1;
        rev_info.no_commit_id = 1;
@@ -1569,7 +1569,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
                struct rev_info rev_info;
                const char *diff_filter_str = "--diff-filter=AM";
 
-               init_revisions(&rev_info, NULL);
+               repo_init_revisions(the_repository, &rev_info, NULL);
                rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
                diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
                add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
@@ -1608,7 +1608,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
                o.verbosity = 0;
 
        if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
-               rerere(state->allow_rerere_autoupdate);
+               repo_rerere(the_repository, state->allow_rerere_autoupdate);
                free(their_tree_name);
                return error(_("Failed to merge in the changes."));
        }
@@ -1903,7 +1903,7 @@ static void am_resolve(struct am_state *state)
                        goto next;
        }
 
-       rerere(0);
+       repo_rerere(the_repository, 0);
 
        do_commit(state);
 
@@ -2328,7 +2328,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
        /* Ensure a valid committer ident can be constructed */
        git_committer_info(IDENT_STRICT);
 
-       if (read_index_preload(&the_index, NULL) < 0)
+       if (read_index_preload(&the_index, NULL, 0) < 0)
                die(_("failed to read the index"));
 
        if (in_progress) {
index e74f675390d975e3a8993037796aa6ef2f975cb7..d2455237ce04d68de624ed60157dc90a4ee4e477 100644 (file)
@@ -97,6 +97,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, local_opts, NULL,
                             PARSE_OPT_KEEP_ALL);
 
+       init_archivers();
+
        if (output)
                create_output_file(output);
 
index c2da673ac802b02609d54de3932fa8a2db221dbd..a443af9ee9dad5ec20de7e75640947c221e7aa6a 100644 (file)
@@ -830,7 +830,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
        setup_default_color_by_age();
        git_config(git_blame_config, &output_option);
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        revs.date_mode = blame_date_mode;
        revs.diffopt.flags.allow_textconv = 1;
        revs.diffopt.flags.follow_renames = 1;
@@ -1001,7 +1001,7 @@ parse_done:
                long bottom, top;
                if (parse_range_arg(range_list.items[range_i].string,
                                    nth_line_cb, &sb, lno, anchor,
-                                   &bottom, &top, sb.path))
+                                   &bottom, &top, sb.path, &the_index))
                        usage(blame_usage);
                if ((!lno && (top || bottom)) || lno < bottom)
                        die(Q_("file %s has only %lu line",
index c396c41533c38630378db370f1db30e99aad849b..0c55f7f065d6ceb34d27a165160192fabe9f22d1 100644 (file)
@@ -716,8 +716,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 0);
                return 0;
-       }
-       else if (edit_description) {
+       } else if (edit_description) {
                const char *branch_name;
                struct strbuf branch_ref = STRBUF_INIT;
 
@@ -809,11 +808,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                git_config_set_multivar(buf.buf, NULL, NULL, 1);
                strbuf_release(&buf);
        } else if (argc > 0 && argc <= 2) {
-               struct branch *branch = branch_get(argv[0]);
-
-               if (!branch)
-                       die(_("no such branch '%s'"), argv[0]);
-
                if (filter.kind != FILTER_REFS_BRANCHES)
                        die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
 
index 64ec1745ab2c20ef18a9292ef3b3c82efd46de17..8d97c84725cd0f7f5347e8ca808072bd227d4d1c 100644 (file)
@@ -113,7 +113,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                        die("git cat-file --textconv %s: <object> must be <sha1:path>",
                            obj_name);
 
-               if (textconv_object(path, obj_context.mode, &oid, 1, &buf, &size))
+               if (textconv_object(the_repository, path, obj_context.mode,
+                                   &oid, 1, &buf, &size))
                        break;
                /* else fallthrough */
 
@@ -305,7 +306,8 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
                                            oid_to_hex(oid), data->rest);
                        } else if (opt->cmdmode == 'c') {
                                enum object_type type;
-                               if (!textconv_object(data->rest, 0100644, oid,
+                               if (!textconv_object(the_repository,
+                                                    data->rest, 0100644, oid,
                                                     1, &contents, &size))
                                        contents = read_object_file(oid,
                                                                    &type,
index 902c06702c62258384692f94ed6b55ecf6381a4e..acdafc6e4c4d104aadcbe7492a8045e16e83ba50 100644 (file)
@@ -214,7 +214,8 @@ static int checkout_merged(int pos, const struct checkout *state)
         * merge.renormalize set, too
         */
        status = ll_merge(&result_buf, path, &ancestor, "base",
-                         &ours, "ours", &theirs, "theirs", NULL);
+                         &ours, "ours", &theirs, "theirs",
+                         state->istate, NULL);
        free(ancestor.ptr);
        free(ours.ptr);
        free(theirs.ptr);
@@ -397,7 +398,7 @@ static void show_local_changes(struct object *head,
 {
        struct rev_info rev;
        /* I think we want full paths, even if we're in a subdirectory. */
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        rev.diffopt.flags = opts->flags;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
        diff_setup_done(&rev.diffopt);
@@ -899,7 +900,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
        struct rev_info revs;
        struct object *object = &old_commit->object;
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        setup_revisions(0, NULL, &revs, NULL);
 
        object->flags &= ~UNINTERESTING;
index 22b974f4b434913cf3ebe9e4073d2cb847f8a468..c02a3f1221d625ad6407317199cc35c7a56a09ba 100644 (file)
@@ -64,6 +64,7 @@ static int graph_verify(int argc, const char **argv)
        if (!graph)
                return 0;
 
+       UNLEAK(graph);
        return verify_commit_graph(the_repository, graph);
 }
 
@@ -89,10 +90,8 @@ static int graph_read(int argc, const char **argv)
        graph_name = get_commit_graph_filename(opts.obj_dir);
        graph = load_commit_graph_one(graph_name);
 
-       if (!graph) {
-               UNLEAK(graph_name);
+       if (!graph)
                die("graph file %s does not exist", graph_name);
-       }
 
        FREE_AND_NULL(graph_name);
 
@@ -115,7 +114,7 @@ static int graph_read(int argc, const char **argv)
                printf(" large_edges");
        printf("\n");
 
-       free_commit_graph(graph);
+       UNLEAK(graph);
 
        return 0;
 }
@@ -170,6 +169,8 @@ static int graph_write(int argc, const char **argv)
                        pack_indexes = &lines;
                if (opts.stdin_commits)
                        commit_hex = &lines;
+
+               UNLEAK(buf);
        }
 
        write_commit_graph(opts.obj_dir,
@@ -178,7 +179,7 @@ static int graph_write(int argc, const char **argv)
                           opts.append,
                           1);
 
-       string_list_clear(&lines, 0);
+       UNLEAK(lines);
        return 0;
 }
 
index 1d5292e4d827312d3a08ceb44f6a120cbd0c4576..074bd9a55160a8efcaa1c108afcf6bd6bc360626 100644 (file)
@@ -508,8 +508,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
 
        wt_status_collect(s);
        wt_status_print(s);
+       wt_status_collect_free_buffers(s);
 
-       return s->commitable;
+       return s->committable;
 }
 
 static int is_a_merge(const struct commit *current_head)
@@ -655,7 +656,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 {
        struct stat statbuf;
        struct strbuf committer_ident = STRBUF_INIT;
-       int commitable;
+       int committable;
        struct strbuf sb = STRBUF_INIT;
        const char *hook_arg1 = NULL;
        const char *hook_arg2 = NULL;
@@ -872,7 +873,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
                saved_color_setting = s->use_color;
                s->use_color = 0;
-               commitable = run_status(s->fp, index_file, prefix, 1, s);
+               committable = run_status(s->fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
                string_list_clear(&s->change, 1);
        } else {
@@ -891,7 +892,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                        for (i = 0; i < active_nr; i++)
                                if (ce_intent_to_add(active_cache[i]))
                                        ita_nr++;
-                       commitable = active_nr - ita_nr > 0;
+                       committable = active_nr - ita_nr > 0;
                } else {
                        /*
                         * Unless the user did explicitly request a submodule
@@ -907,7 +908,7 @@ 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;
-                       commitable = index_differs_from(parent, &flags, 1);
+                       committable = index_differs_from(parent, &flags, 1);
                }
        }
        strbuf_release(&committer_ident);
@@ -919,7 +920,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
         * explicit --allow-empty. In the cherry-pick case, it may be
         * empty due to conflict resolution, which the user should okay.
         */
-       if (!commitable && whence != FROM_MERGE && !allow_empty &&
+       if (!committable && whence != FROM_MERGE && !allow_empty &&
            !(amend && is_a_merge(current_head))) {
                s->display_comment_prefix = old_display_comment_prefix;
                run_status(stdout, index_file, prefix, 0, s);
@@ -983,7 +984,7 @@ static const char *find_author_by_nickname(const char *name)
        const char *av[20];
        int ac = 0;
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        strbuf_addf(&buf, "--author=%s", name);
        av[++ac] = "--all";
        av[++ac] = "-i";
@@ -1189,14 +1190,14 @@ static int parse_and_validate_options(int argc, const char *argv[],
 static int dry_run_commit(int argc, const char **argv, const char *prefix,
                          const struct commit *current_head, struct wt_status *s)
 {
-       int commitable;
+       int committable;
        const char *index_file;
 
        index_file = prepare_index(argc, argv, prefix, current_head, 1);
-       commitable = run_status(stdout, index_file, prefix, 0, s);
+       committable = run_status(stdout, index_file, prefix, 0, s);
        rollback_index_files();
 
-       return commitable ? 0 : 1;
+       return committable ? 0 : 1;
 }
 
 define_list_config_array_extra(color_status_slots, {"added"});
@@ -1298,6 +1299,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        static int no_renames = -1;
        static const char *rename_score_arg = (const char *)-1;
        static struct wt_status s;
+       unsigned int progress_flag = 0;
        int fd;
        struct object_id oid;
        static struct option builtin_status_options[] = {
@@ -1358,8 +1360,13 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                       PATHSPEC_PREFER_FULL,
                       prefix, argv);
 
-       read_cache_preload(&s.pathspec);
-       refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
+       if (status_format != STATUS_FORMAT_PORCELAIN &&
+           status_format != STATUS_FORMAT_PORCELAIN_V2)
+               progress_flag = REFRESH_PROGRESS;
+       read_index_preload(&the_index, &s.pathspec, progress_flag);
+       refresh_index(&the_index,
+                     REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
+                     &s.pathspec, NULL, NULL);
 
        if (use_optional_locks())
                fd = hold_locked_index(&index_lock, 0);
@@ -1391,6 +1398,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                s.prefix = prefix;
 
        wt_status_print(&s);
+       wt_status_collect_free_buffers(&s);
+
        return 0;
 }
 
@@ -1657,7 +1666,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
                write_commit_graph_reachable(get_object_directory(), 0, 0);
 
-       rerere(0);
+       repo_rerere(the_repository, 0);
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
        run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
        if (amend && !no_post_rewrite) {
index 22c0541da552c8135e21a6f3c9d3f7829fc0d31a..c48c34e8667c36a42101fffe51bd96f1a4d36842 100644 (file)
@@ -488,7 +488,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)
                "--objects", "--in-commit-order", "--reverse", "HEAD",
                NULL);
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
                BUG("setup_revisions could not handle all args?");
 
@@ -636,7 +636,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                        if (0 <= fd)
                                update_index_if_able(&the_index, &index_lock);
 
-                       init_revisions(&revs, prefix);
+                       repo_init_revisions(the_repository, &revs, prefix);
                        argv_array_pushv(&args, diff_index_args);
                        if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1)
                                BUG("malformed internal diff-index command line");
index e88493ffe5d5d66461edb71832b58e8b3b4b2bc8..48cfcb935d0413fcf3850e0c28ec237b642b38a2 100644 (file)
@@ -25,7 +25,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
                usage(diff_files_usage);
 
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        rev.abbrev = 0;
        precompose_argv(argc, argv);
 
index 522f4fdffd064de2e50232934cb7d616246c92e4..fcccd1f10dc95cd47a994b5139226af438049f87 100644 (file)
@@ -22,7 +22,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
                usage(diff_cache_usage);
 
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        rev.abbrev = 0;
        precompose_argv(argc, argv);
 
index d07bf2e4c4b5c2ec2f93930c0dd16897df32b25f..ef996126d7b521ea1de2c837e4ed97fdcb6f1e71 100644 (file)
@@ -110,7 +110,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                usage(diff_tree_usage);
 
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
-       init_revisions(opt, prefix);
+       repo_init_revisions(the_repository, opt, prefix);
        if (read_cache() < 0)
                die(_("index file corrupt"));
        opt->abbrev = 0;
index b3a8ba488ff824e2feda6b24df362f662fe5ddde..f0393bba23a7d95c67470508a6b63e4e41e8d46b 100644 (file)
@@ -318,7 +318,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        git_config(git_diff_ui_config, NULL);
        precompose_argv(argc, argv);
 
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
 
        if (no_index && argc != i + 2) {
                if (no_index == DIFF_NO_INDEX_IMPLICIT) {
@@ -339,7 +339,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        }
        if (no_index)
                /* If this is a no-index diff, just run it and exit there. */
-               diff_no_index(&rev, argc, argv);
+               diff_no_index(the_repository, &rev, argc, argv);
 
        /* Otherwise, we are doing the usual "git" diff */
        rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
index b41a9199ff441579655c91fc692211257e4e91ed..544b0e86397cb98ddfdad3cffdd983905ee36029 100644 (file)
@@ -112,7 +112,7 @@ static int use_wt_file(const char *workdir, const char *name,
                int fd = open(buf.buf, O_RDONLY);
 
                if (fd >= 0 &&
-                   !index_fd(&wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
+                   !index_fd(&the_index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
                        if (is_null_oid(oid)) {
                                oidcpy(oid, &wt_oid);
                                use = 1;
index 74f3bf5c96974a5f76c8b50c26915b582e780bb7..456797c12a41a468a0b6dd25153403f6cfd7bb2f 100644 (file)
@@ -1033,7 +1033,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        /* we handle encodings */
        git_config(git_default_config, NULL);
 
-       init_revisions(&revs, prefix);
+       repo_init_revisions(the_repository, &revs, prefix);
        init_revision_sources(&revision_sources);
        revs.topo_order = 1;
        revs.sources = &revision_sources;
index 1a1bc63566b44bc83c8429463104615d1b2117ff..63e69a58011a4d3c774cd3c81ae9f10e3a96efe6 100644 (file)
@@ -16,13 +16,14 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
 {
        struct ref *ref;
        struct object_id oid;
+       const char *p;
 
-       if (!get_oid_hex(name, &oid)) {
-               if (name[GIT_SHA1_HEXSZ] == ' ') {
-                       /* <sha1> <ref>, find refname */
-                       name += GIT_SHA1_HEXSZ + 1;
-               } else if (name[GIT_SHA1_HEXSZ] == '\0') {
-                       ; /* <sha1>, leave sha1 as name */
+       if (!parse_oid_hex(name, &oid, &p)) {
+               if (*p == ' ') {
+                       /* <oid> <ref>, find refname */
+                       name = p + 1;
+               } else if (*p == '\0') {
+                       ; /* <oid>, leave oid as name */
                } else {
                        /* <ref>, clear cruft from oid */
                        oidclr(&oid);
index 0696abfc2a158baa3177fa9e71e2c16bc64655a4..8f7249f2b138279dabfe994248f58069e8d52a7b 100644 (file)
@@ -931,10 +931,11 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
  * everything we are going to fetch already exists and is connected
  * locally.
  */
-static int quickfetch(struct ref *ref_map)
+static int check_exist_and_connected(struct ref *ref_map)
 {
        struct ref *rm = ref_map;
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
+       struct ref *r;
 
        /*
         * If we are deepening a shallow clone we already have these
@@ -945,13 +946,23 @@ static int quickfetch(struct ref *ref_map)
         */
        if (deepen)
                return -1;
+
+       /*
+        * check_connected() allows objects to merely be promised, but
+        * we need all direct targets to exist.
+        */
+       for (r = rm; r; r = r->next) {
+               if (!has_object_file(&r->old_oid))
+                       return -1;
+       }
+
        opt.quiet = 1;
        return check_connected(iterate_ref_map, &rm, &opt);
 }
 
 static int fetch_refs(struct transport *transport, struct ref *ref_map)
 {
-       int ret = quickfetch(ref_map);
+       int ret = check_exist_and_connected(ref_map);
        if (ret)
                ret = transport_fetch_refs(transport, ref_map);
        if (!ret)
@@ -1175,6 +1186,7 @@ static int do_fetch(struct transport *transport,
        int retcode = 0;
        const struct ref *remote_refs;
        struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+       int must_list_refs = 1;
 
        if (tags == TAGS_DEFAULT) {
                if (transport->remote->fetch_tags == 2)
@@ -1190,17 +1202,36 @@ static int do_fetch(struct transport *transport,
                        goto cleanup;
        }
 
-       if (rs->nr)
+       if (rs->nr) {
+               int i;
+
                refspec_ref_prefixes(rs, &ref_prefixes);
-       else if (transport->remote && transport->remote->fetch.nr)
+
+               /*
+                * We can avoid listing refs if all of them are exact
+                * OIDs
+                */
+               must_list_refs = 0;
+               for (i = 0; i < rs->nr; i++) {
+                       if (!rs->items[i].exact_sha1) {
+                               must_list_refs = 1;
+                               break;
+                       }
+               }
+       } else if (transport->remote && transport->remote->fetch.nr)
                refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
 
-       if (ref_prefixes.argc &&
-           (tags == TAGS_SET || (tags == TAGS_DEFAULT))) {
-               argv_array_push(&ref_prefixes, "refs/tags/");
+       if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
+               must_list_refs = 1;
+               if (ref_prefixes.argc)
+                       argv_array_push(&ref_prefixes, "refs/tags/");
        }
 
-       remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+       if (must_list_refs)
+               remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+       else
+               remote_refs = NULL;
+
        argv_array_clear(&ref_prefixes);
 
        ref_map = get_ref_map(transport->remote, remote_refs, rs,
index 59a40342b675130de6a2ddd76451bff8818e9623..a4615587fd7929e8fb49e65dbda30a40b8599cfd 100644 (file)
@@ -643,7 +643,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                struct rev_info rev;
 
                head = lookup_commit_or_die(&head_oid, "HEAD");
-               init_revisions(&rev, NULL);
+               repo_init_revisions(the_repository, &rev, NULL);
                rev.commit_format = CMIT_FMT_ONELINE;
                rev.ignore_merges = 1;
                rev.limited = 1;
index 601f801158f097b302dcf6615016bb4cefbc0225..d8508ddf792dd22dc0c8c283df29e5e6d9dacbf7 100644 (file)
@@ -103,7 +103,8 @@ static void add_work(struct grep_opt *opt, const struct grep_source *gs)
 
        todo[todo_end].source = *gs;
        if (opt->binary != GREP_BINARY_TEXT)
-               grep_source_load_driver(&todo[todo_end].source);
+               grep_source_load_driver(&todo[todo_end].source,
+                                       opt->repo->index);
        todo[todo_end].done = 0;
        strbuf_reset(&todo[todo_end].out);
        todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
@@ -811,6 +812,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        GREP_BINARY_NOMATCH),
                OPT_BOOL(0, "textconv", &opt.allow_textconv,
                         N_("process binary files with textconv filters")),
+               OPT_SET_INT('r', "recursive", &opt.max_depth,
+                           N_("search in subdirectories (default)"), -1),
                { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"),
                        N_("descend at most <depth> levels"), PARSE_OPT_NONEG,
                        NULL, 1 },
@@ -904,9 +907,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       init_grep_defaults();
+       init_grep_defaults(the_repository);
        git_config(grep_cmd_config, NULL);
-       grep_init(&opt, prefix);
+       grep_init(&opt, the_repository, prefix);
 
        /*
         * If there is no -- then the paths must exist in the working
index 9ada4f4dfd8747d3888203749f057915f7c9ab46..d6f06ea32f51272efa5ccb3fdd396b5916e98e45 100644 (file)
@@ -40,7 +40,7 @@ static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
        if (fstat(fd, &st) < 0 ||
            (literally
             ? hash_literally(&oid, fd, type, flags)
-            : index_fd(&oid, fd, &st, type_from_string(type), path, flags)))
+            : index_fd(&the_index, &oid, fd, &st, type_from_string(type), path, flags)))
                die((flags & HASH_WRITE_OBJECT)
                    ? "Unable to add %s to database"
                    : "Unable to hash %s", path);
index 8d4f6dd30152e70d1379a956793b1086fba7f5b6..7739a5c1551426fd2302aaf5daa10e454d174195 100644 (file)
@@ -38,7 +38,7 @@ static const char *html_path;
 static int show_all = 0;
 static int show_guides = 0;
 static int show_config;
-static int verbose;
+static int verbose = 1;
 static unsigned int colopts;
 static enum help_format help_format = HELP_FORMAT_NONE;
 static int exclude_guides;
@@ -415,9 +415,37 @@ static const char *check_git_cmd(const char* cmd)
 
        alias = alias_lookup(cmd);
        if (alias) {
-               printf_ln(_("'%s' is aliased to '%s'"), cmd, alias);
-               free(alias);
-               exit(0);
+               const char **argv;
+               int count;
+
+               /*
+                * handle_builtin() in git.c rewrites "git cmd --help"
+                * to "git help --exclude-guides cmd", so we can use
+                * exclude_guides to distinguish "git cmd --help" from
+                * "git help cmd". In the latter case, or if cmd is an
+                * alias for a shell command, just print the alias
+                * definition.
+                */
+               if (!exclude_guides || alias[0] == '!') {
+                       printf_ln(_("'%s' is aliased to '%s'"), cmd, alias);
+                       free(alias);
+                       exit(0);
+               }
+               /*
+                * Otherwise, we pretend that the command was "git
+                * word0 --help". We use split_cmdline() to get the
+                * first word of the alias, to ensure that we use the
+                * same rules as when the alias is actually
+                * used. split_cmdline() modifies alias in-place.
+                */
+               fprintf_ln(stderr, _("'%s' is aliased to '%s'"), cmd, alias);
+               count = split_cmdline(alias, &argv);
+               if (count < 0)
+                       die(_("bad alias.%s string: %s"), cmd,
+                           split_cmdline_strerror(count));
+               free(argv);
+               UNLEAK(alias);
+               return alias;
        }
 
        if (exclude_guides)
index 1dbb9d829bffcb25739737783c4fd4fd734f5940..061d4fd864f3fb479e2877e3bbf25928ad1fb9d3 100644 (file)
@@ -118,7 +118,7 @@ static int log_line_range_callback(const struct option *option, const char *arg,
 
 static void init_log_defaults(void)
 {
-       init_grep_defaults();
+       init_grep_defaults(the_repository);
        init_diff_ui_defaults();
 
        decoration_style = auto_decoration_style();
@@ -470,7 +470,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
        init_log_defaults();
        git_config(git_log_config, NULL);
 
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        rev.diff = 1;
        rev.simplify_history = 0;
        memset(&opt, 0, sizeof(opt));
@@ -510,7 +510,8 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
                                 &oidc, &obj_context))
                die(_("Not a valid object name %s"), obj_name);
        if (!obj_context.path ||
-           !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) {
+           !textconv_object(the_repository, obj_context.path,
+                            obj_context.mode, &oidc, 1, &buf, &size)) {
                free(obj_context.path);
                return stream_blob_to_fd(1, oid, NULL, 0);
        }
@@ -587,7 +588,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
        git_config(git_log_config, NULL);
 
        memset(&match_all, 0, sizeof(match_all));
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        rev.diff = 1;
        rev.always_show_header = 1;
        rev.no_walk = REVISION_WALK_NO_WALK_SORTED;
@@ -667,7 +668,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
        init_log_defaults();
        git_config(git_log_config, NULL);
 
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        init_reflog_walk(&rev.reflog_info);
        rev.verbose_header = 1;
        memset(&opt, 0, sizeof(opt));
@@ -706,7 +707,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
        init_log_defaults();
        git_config(git_log_config, NULL);
 
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        rev.always_show_header = 1;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
@@ -916,10 +917,10 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
        if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
                die(_("Not a range."));
 
-       init_patch_ids(ids);
+       init_patch_ids(the_repository, ids);
 
        /* given a range a..b get all patch ids for b..a */
-       init_revisions(&check_rev, rev->prefix);
+       repo_init_revisions(the_repository, &check_rev, rev->prefix);
        check_rev.max_parents = 1;
        o1->flags ^= UNINTERESTING;
        o2->flags ^= UNINTERESTING;
@@ -1377,13 +1378,13 @@ static void prepare_bases(struct base_tree_info *bases,
                return;
 
        init_commit_base(&commit_base);
-       diff_setup(&diffopt);
+       repo_diff_setup(the_repository, &diffopt);
        diffopt.flags.recursive = 1;
        diff_setup_done(&diffopt);
 
        oidcpy(&bases->base_commit, &base->object.oid);
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        revs.max_parents = 1;
        revs.topo_order = 1;
        for (i = 0; i < total; i++) {
@@ -1588,7 +1589,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        extra_cc.strdup_strings = 1;
        init_log_defaults();
        git_config(git_format_config, NULL);
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.expand_tabs_in_log_default = 0;
        rev.verbose_header = 1;
@@ -2038,7 +2039,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                }
        }
 
-       init_revisions(&revs, prefix);
+       repo_init_revisions(the_repository, &revs, prefix);
        revs.max_parents = 1;
 
        if (add_pending_commit(head, &revs, 0))
index 1a25df7ee15b45df142679286afdb0e8c55647dc..6a0cdec30d2d77711d880f5e4b6ba6ff981e2e8d 100644 (file)
@@ -151,6 +151,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        }
 
        UNLEAK(sorting);
-       UNLEAK(ref_array);
+       ref_array_clear(&ref_array);
        return status;
 }
index 1c920990701381452da910e4cee66be5e6fde5a0..e3f8da13b69b8dbe6898bf71ee2b5df206179bd1 100644 (file)
@@ -111,54 +111,12 @@ static int handle_is_ancestor(int argc, const char **argv)
                return 1;
 }
 
-struct rev_collect {
-       struct commit **commit;
-       int nr;
-       int alloc;
-       unsigned int initial : 1;
-};
-
-static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
-{
-       struct commit *commit;
-
-       if (is_null_oid(oid))
-               return;
-
-       commit = lookup_commit(the_repository, oid);
-       if (!commit ||
-           (commit->object.flags & TMP_MARK) ||
-           parse_commit(commit))
-               return;
-
-       ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
-       revs->commit[revs->nr++] = commit;
-       commit->object.flags |= TMP_MARK;
-}
-
-static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-                                 const char *ident, timestamp_t timestamp,
-                                 int tz, const char *message, void *cbdata)
-{
-       struct rev_collect *revs = cbdata;
-
-       if (revs->initial) {
-               revs->initial = 0;
-               add_one_commit(ooid, revs);
-       }
-       add_one_commit(noid, revs);
-       return 0;
-}
-
 static int handle_fork_point(int argc, const char **argv)
 {
        struct object_id oid;
        char *refname;
+       struct commit *derived, *fork_point;
        const char *commitname;
-       struct rev_collect revs;
-       struct commit *derived;
-       struct commit_list *bases;
-       int i, ret = 0;
 
        switch (dwim_ref(argv[0], strlen(argv[0]), &oid, &refname)) {
        case 0:
@@ -174,41 +132,14 @@ static int handle_fork_point(int argc, const char **argv)
                die("Not a valid object name: '%s'", commitname);
 
        derived = lookup_commit_reference(the_repository, &oid);
-       memset(&revs, 0, sizeof(revs));
-       revs.initial = 1;
-       for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
 
-       if (!revs.nr && !get_oid(refname, &oid))
-               add_one_commit(&oid, &revs);
+       fork_point = get_fork_point(refname, derived);
 
-       for (i = 0; i < revs.nr; i++)
-               revs.commit[i]->object.flags &= ~TMP_MARK;
-
-       bases = get_merge_bases_many_dirty(derived, revs.nr, revs.commit);
-
-       /*
-        * There should be one and only one merge base, when we found
-        * a common ancestor among reflog entries.
-        */
-       if (!bases || bases->next) {
-               ret = 1;
-               goto cleanup_return;
-       }
-
-       /* And the found one must be one of the reflog entries */
-       for (i = 0; i < revs.nr; i++)
-               if (&bases->item->object == &revs.commit[i]->object)
-                       break; /* found */
-       if (revs.nr <= i) {
-               ret = 1; /* not found */
-               goto cleanup_return;
-       }
-
-       printf("%s\n", oid_to_hex(&bases->item->object.oid));
+       if (!fork_point)
+               return 1;
 
-cleanup_return:
-       free_commit_list(bases);
-       return ret;
+       printf("%s\n", oid_to_hex(&fork_point->object.oid));
+       return 0;
 }
 
 int cmd_merge_base(int argc, const char **argv, const char *prefix)
index 8cea8a74f2b7e89dfedca3688f2b9d8863055010..8fc108d305a33c4ba615837b60bda8175f2fb882 100644 (file)
@@ -76,7 +76,7 @@ static void *result(struct merge_list *entry, unsigned long *size)
        their = NULL;
        if (entry)
                their = entry->blob;
-       return merge_blobs(path, base, our, their, size);
+       return merge_blobs(&the_index, path, base, our, their, size);
 }
 
 static void *origin(struct merge_list *entry, unsigned long *size)
index e331ca6d481005f6122fa6fda6075728f893632d..4aa60715980f2f226d752e9d7fe0df4ecdcc369d 100644 (file)
@@ -390,7 +390,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
 
        printf(_("Squash commit -- not updating HEAD\n"));
 
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        rev.ignore_merges = 1;
        rev.commit_format = CMIT_FMT_MEDIUM;
 
@@ -453,7 +453,7 @@ static void finish(struct commit *head_commit,
        }
        if (new_head && show_diffstat) {
                struct diff_options opts;
-               diff_setup(&opts);
+               repo_diff_setup(the_repository, &opts);
                opts.stat_width = -1; /* use full terminal width */
                opts.stat_graph_width = -1; /* respect statGraphWidth config */
                opts.output_format |=
@@ -729,8 +729,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                        die(_("unable to write %s"), get_index_file());
                return clean ? 0 : 1;
        } else {
-               return try_merge_command(strategy, xopts_nr, xopts,
-                                               common, head_arg, remoteheads);
+               return try_merge_command(the_repository,
+                                        strategy, xopts_nr, xopts,
+                                        common, head_arg, remoteheads);
        }
 }
 
@@ -899,7 +900,7 @@ static int suggest_conflicts(void)
        fputs(msgbuf.buf, fp);
        strbuf_release(&msgbuf);
        fclose(fp);
-       rerere(allow_rerere_auto);
+       repo_rerere(the_repository, allow_rerere_auto);
        printf(_("Automatic merge failed; "
                        "fix conflicts and then commit the result.\n"));
        return 1;
@@ -911,7 +912,7 @@ static int evaluate_result(void)
        struct rev_info rev;
 
        /* Check how many files differ. */
-       init_revisions(&rev, "");
+       repo_init_revisions(the_repository, &rev, "");
        setup_revisions(0, NULL, &rev, NULL);
        rev.diffopt.output_format |=
                DIFF_FORMAT_CALLBACK;
@@ -1471,7 +1472,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        goto done;
                }
 
-               if (checkout_fast_forward(&head_commit->object.oid,
+               if (checkout_fast_forward(the_repository,
+                                         &head_commit->object.oid,
                                          &commit->object.oid,
                                          overwrite_ignore)) {
                        ret = 1;
index 2dc4ad6ba8f227a6a8a46bac62048959220411ec..94e82b8504f7da5bed50d81771cefe3445b98ece 100644 (file)
@@ -98,7 +98,7 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss
 
        *ntr++ = 0; /* now at the beginning of SHA1 */
 
-       path = ntr + 41;  /* at the beginning of name */
+       path = (char *)p + 1;  /* at the beginning of name */
        if (!nul_term_line && path[0] == '"') {
                struct strbuf p_uq = STRBUF_INIT;
                if (unquote_c_style(&p_uq, path, NULL))
index c6370f2716a7d5a9f78ea6e6627a87b40ab32cc9..e50c6cd1ff25ce4d65e0ebd854d052c3d1160a2a 100644 (file)
@@ -2399,7 +2399,6 @@ static void init_threaded_search(void)
        pthread_mutex_init(&cache_mutex, NULL);
        pthread_mutex_init(&progress_mutex, NULL);
        pthread_cond_init(&progress_cond, NULL);
-       pthread_mutex_init(&to_pack.lock, NULL);
        old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
 }
 
@@ -3106,7 +3105,7 @@ static void get_object_list(int ac, const char **av)
        char line[1000];
        int flags = 0;
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        save_commit_buffer = 0;
        setup_revisions(ac, av, &revs, NULL);
 
index 4916a4daa264ee695dbfbde81dbd8ef0072ac9c4..1ec9ddd751df6644d2c39ace41a1494800082638 100644 (file)
@@ -120,7 +120,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        save_commit_buffer = 0;
        read_replace_refs = 0;
        ref_paranoia = 1;
-       init_revisions(&revs, prefix);
+       repo_init_revisions(the_repository, &revs, prefix);
 
        argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
 
@@ -161,7 +161,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        free(s);
 
        if (is_repository_shallow(the_repository))
-               prune_shallow(show_only);
+               prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
 
        return 0;
 }
index b2055d1dd6f180ec2383ee6f569045e5a55cb4b4..798ecf7faf186ba337cc73e945ac0c7fefd94866 100644 (file)
@@ -563,7 +563,9 @@ static int pull_into_void(const struct object_id *merge_head,
         * index/worktree changes that the user already made on the unborn
         * branch.
         */
-       if (checkout_fast_forward(the_hash_algo->empty_tree, merge_head, 0))
+       if (checkout_fast_forward(the_repository,
+                                 the_hash_algo->empty_tree,
+                                 merge_head, 0))
                return 1;
 
        if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
@@ -916,7 +918,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
                        "fast-forwarding your working tree from\n"
                        "commit %s."), oid_to_hex(&orig_head));
 
-               if (checkout_fast_forward(&orig_head, &curr_head, 0))
+               if (checkout_fast_forward(the_repository, &orig_head,
+                                         &curr_head, 0))
                        die(_("Cannot fast-forward your working tree.\n"
                                "After making sure that you saved anything precious from\n"
                                "$ git diff %s\n"
@@ -942,7 +945,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
                int ret = 0;
                if ((recurse_submodules == RECURSE_SUBMODULES_ON ||
                     recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) &&
-                   submodule_touches_in_range(&rebase_fork_point, &curr_head))
+                   submodule_touches_in_range(&the_index, &rebase_fork_point, &curr_head))
                        die(_("cannot rebase with locally recorded submodule modifications"));
                if (!autostash) {
                        struct commit_list *list = NULL;
index 96af5374937e5b8b99343e8d324e54e98fb11743..f01a0be8513412789ca49e28b0cc2519b7c04760 100644 (file)
@@ -28,7 +28,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
 
        git_config(git_diff_ui_config, NULL);
 
-       diff_setup(&diffopt);
+       repo_diff_setup(the_repository, &diffopt);
 
        argc = parse_options(argc, argv, NULL, options,
                             builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN |
diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
deleted file mode 100644 (file)
index f7c2a5f..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "config.h"
-#include "parse-options.h"
-#include "sequencer.h"
-
-static const char * const builtin_rebase_helper_usage[] = {
-       N_("git rebase--helper [<options>]"),
-       NULL
-};
-
-int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
-{
-       struct replay_opts opts = REPLAY_OPTS_INIT;
-       unsigned flags = 0, keep_empty = 0, rebase_merges = 0;
-       int abbreviate_commands = 0, rebase_cousins = -1;
-       enum {
-               CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
-               CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
-               ADD_EXEC
-       } command = 0;
-       struct option options[] = {
-               OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
-               OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")),
-               OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
-                       N_("allow commits with empty messages")),
-               OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")),
-               OPT_BOOL(0, "rebase-cousins", &rebase_cousins,
-                        N_("keep original branch points of cousins")),
-               OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
-                               CONTINUE),
-               OPT_CMDMODE(0, "abort", &command, N_("abort rebase"),
-                               ABORT),
-               OPT_CMDMODE(0, "make-script", &command,
-                       N_("make rebase script"), MAKE_SCRIPT),
-               OPT_CMDMODE(0, "shorten-ids", &command,
-                       N_("shorten commit ids in the todo list"), SHORTEN_OIDS),
-               OPT_CMDMODE(0, "expand-ids", &command,
-                       N_("expand commit ids in the todo list"), EXPAND_OIDS),
-               OPT_CMDMODE(0, "check-todo-list", &command,
-                       N_("check the todo list"), CHECK_TODO_LIST),
-               OPT_CMDMODE(0, "skip-unnecessary-picks", &command,
-                       N_("skip unnecessary picks"), SKIP_UNNECESSARY_PICKS),
-               OPT_CMDMODE(0, "rearrange-squash", &command,
-                       N_("rearrange fixup/squash lines"), REARRANGE_SQUASH),
-               OPT_CMDMODE(0, "add-exec-commands", &command,
-                       N_("insert exec commands in todo list"), ADD_EXEC),
-               OPT_END()
-       };
-
-       sequencer_init_config(&opts);
-       git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
-
-       opts.action = REPLAY_INTERACTIVE_REBASE;
-       opts.allow_ff = 1;
-       opts.allow_empty = 1;
-
-       argc = parse_options(argc, argv, NULL, options,
-                       builtin_rebase_helper_usage, PARSE_OPT_KEEP_ARGV0);
-
-       flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
-       flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
-       flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
-       flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
-       flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
-
-       if (rebase_cousins >= 0 && !rebase_merges)
-               warning(_("--[no-]rebase-cousins has no effect without "
-                         "--rebase-merges"));
-
-       if (command == CONTINUE && argc == 1)
-               return !!sequencer_continue(&opts);
-       if (command == ABORT && argc == 1)
-               return !!sequencer_remove_state(&opts);
-       if (command == MAKE_SCRIPT && argc > 1)
-               return !!sequencer_make_script(stdout, argc, argv, flags);
-       if ((command == SHORTEN_OIDS || command == EXPAND_OIDS) && argc == 1)
-               return !!transform_todos(flags);
-       if (command == CHECK_TODO_LIST && argc == 1)
-               return !!check_todo_list();
-       if (command == SKIP_UNNECESSARY_PICKS && argc == 1)
-               return !!skip_unnecessary_picks();
-       if (command == REARRANGE_SQUASH && argc == 1)
-               return !!rearrange_squash();
-       if (command == ADD_EXEC && argc == 2)
-               return !!sequencer_add_exec_commands(argv[1]);
-       usage_with_options(builtin_rebase_helper_usage, options);
-}
diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
new file mode 100644 (file)
index 0000000..a2ab68e
--- /dev/null
@@ -0,0 +1,271 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "parse-options.h"
+#include "sequencer.h"
+#include "rebase-interactive.h"
+#include "argv-array.h"
+#include "refs.h"
+#include "rerere.h"
+#include "run-command.h"
+
+static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
+static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
+static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
+
+static int get_revision_ranges(const char *upstream, const char *onto,
+                              const char **head_hash,
+                              char **revisions, char **shortrevisions)
+{
+       const char *base_rev = upstream ? upstream : onto, *shorthead;
+       struct object_id orig_head;
+
+       if (get_oid("HEAD", &orig_head))
+               return error(_("no HEAD?"));
+
+       *head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ);
+       *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
+
+       shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
+
+       if (upstream) {
+               const char *shortrev;
+               struct object_id rev_oid;
+
+               get_oid(base_rev, &rev_oid);
+               shortrev = find_unique_abbrev(&rev_oid, DEFAULT_ABBREV);
+
+               *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
+       } else
+               *shortrevisions = xstrdup(shorthead);
+
+       return 0;
+}
+
+static int init_basic_state(struct replay_opts *opts, const char *head_name,
+                           const char *onto, const char *orig_head)
+{
+       FILE *interactive;
+
+       if (!is_directory(path_state_dir()) && mkdir_in_gitdir(path_state_dir()))
+               return error_errno(_("could not create temporary %s"), path_state_dir());
+
+       delete_reflog("REBASE_HEAD");
+
+       interactive = fopen(path_interactive(), "w");
+       if (!interactive)
+               return error_errno(_("could not mark as interactive"));
+       fclose(interactive);
+
+       return write_basic_state(opts, head_name, onto, orig_head);
+}
+
+static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,
+                                const char *switch_to, const char *upstream,
+                                const char *onto, const char *onto_name,
+                                const char *squash_onto, const char *head_name,
+                                const char *restrict_revision, char *raw_strategies,
+                                const char *cmd, unsigned autosquash)
+{
+       int ret;
+       const char *head_hash = NULL;
+       char *revisions = NULL, *shortrevisions = NULL;
+       struct argv_array make_script_args = ARGV_ARRAY_INIT;
+       FILE *todo_list;
+
+       if (prepare_branch_to_be_rebased(opts, switch_to))
+               return -1;
+
+       if (get_revision_ranges(upstream, onto, &head_hash,
+                               &revisions, &shortrevisions))
+               return -1;
+
+       if (raw_strategies)
+               parse_strategy_opts(opts, raw_strategies);
+
+       if (init_basic_state(opts, head_name, onto, head_hash)) {
+               free(revisions);
+               free(shortrevisions);
+
+               return -1;
+       }
+
+       if (!upstream && squash_onto)
+               write_file(path_squash_onto(), "%s\n", squash_onto);
+
+       todo_list = fopen(rebase_path_todo(), "w");
+       if (!todo_list) {
+               free(revisions);
+               free(shortrevisions);
+
+               return error_errno(_("could not open %s"), rebase_path_todo());
+       }
+
+       argv_array_pushl(&make_script_args, "", revisions, NULL);
+       if (restrict_revision)
+               argv_array_push(&make_script_args, restrict_revision);
+
+       ret = sequencer_make_script(todo_list,
+                                   make_script_args.argc, make_script_args.argv,
+                                   flags);
+       fclose(todo_list);
+
+       if (ret)
+               error(_("could not generate todo list"));
+       else {
+               discard_cache();
+               ret = complete_action(opts, flags, shortrevisions, onto_name, onto,
+                                     head_hash, cmd, autosquash);
+       }
+
+       free(revisions);
+       free(shortrevisions);
+       argv_array_clear(&make_script_args);
+
+       return ret;
+}
+
+static const char * const builtin_rebase_interactive_usage[] = {
+       N_("git rebase--interactive [<options>]"),
+       NULL
+};
+
+int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
+{
+       struct replay_opts opts = REPLAY_OPTS_INIT;
+       unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
+       int abbreviate_commands = 0, rebase_cousins = -1, ret = 0;
+       const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,
+               *squash_onto = NULL, *upstream = NULL, *head_name = NULL,
+               *switch_to = NULL, *cmd = NULL;
+       char *raw_strategies = NULL;
+       enum {
+               NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,
+               SHORTEN_OIDS, EXPAND_OIDS, CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC
+       } command = 0;
+       struct option options[] = {
+               OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
+               OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")),
+               OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
+                        N_("allow commits with empty messages")),
+               OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")),
+               OPT_BOOL(0, "rebase-cousins", &rebase_cousins,
+                        N_("keep original branch points of cousins")),
+               OPT_BOOL(0, "autosquash", &autosquash,
+                        N_("move commits that begin with squash!/fixup!")),
+               OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
+               OPT__VERBOSE(&opts.verbose, N_("be verbose")),
+               OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
+                           CONTINUE),
+               OPT_CMDMODE(0, "skip", &command, N_("skip commit"), SKIP),
+               OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
+                           EDIT_TODO),
+               OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
+                           SHOW_CURRENT_PATCH),
+               OPT_CMDMODE(0, "shorten-ids", &command,
+                       N_("shorten commit ids in the todo list"), SHORTEN_OIDS),
+               OPT_CMDMODE(0, "expand-ids", &command,
+                       N_("expand commit ids in the todo list"), EXPAND_OIDS),
+               OPT_CMDMODE(0, "check-todo-list", &command,
+                       N_("check the todo list"), CHECK_TODO_LIST),
+               OPT_CMDMODE(0, "rearrange-squash", &command,
+                       N_("rearrange fixup/squash lines"), REARRANGE_SQUASH),
+               OPT_CMDMODE(0, "add-exec-commands", &command,
+                       N_("insert exec commands in todo list"), ADD_EXEC),
+               OPT_STRING(0, "onto", &onto, N_("onto"), N_("onto")),
+               OPT_STRING(0, "restrict-revision", &restrict_revision,
+                          N_("restrict-revision"), N_("restrict revision")),
+               OPT_STRING(0, "squash-onto", &squash_onto, N_("squash-onto"),
+                          N_("squash onto")),
+               OPT_STRING(0, "upstream", &upstream, N_("upstream"),
+                          N_("the upstream commit")),
+               OPT_STRING(0, "head-name", &head_name, N_("head-name"), N_("head name")),
+               { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign, N_("key-id"),
+                       N_("GPG-sign commits"),
+                       PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+               OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
+                          N_("rebase strategy")),
+               OPT_STRING(0, "strategy-opts", &raw_strategies, N_("strategy-opts"),
+                          N_("strategy options")),
+               OPT_STRING(0, "switch-to", &switch_to, N_("switch-to"),
+                          N_("the branch or commit to checkout")),
+               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_END()
+       };
+
+       sequencer_init_config(&opts);
+       git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
+
+       opts.action = REPLAY_INTERACTIVE_REBASE;
+       opts.allow_ff = 1;
+       opts.allow_empty = 1;
+
+       if (argc == 1)
+               usage_with_options(builtin_rebase_interactive_usage, options);
+
+       argc = parse_options(argc, argv, NULL, options,
+                       builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
+
+       opts.gpg_sign = xstrdup_or_null(opts.gpg_sign);
+
+       flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
+       flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
+       flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
+       flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
+       flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
+
+       if (rebase_cousins >= 0 && !rebase_merges)
+               warning(_("--[no-]rebase-cousins has no effect without "
+                         "--rebase-merges"));
+
+       switch (command) {
+       case NONE:
+               if (!onto && !upstream)
+                       die(_("a base commit must be provided with --upstream or --onto"));
+
+               ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,
+                                           onto_name, squash_onto, head_name, restrict_revision,
+                                           raw_strategies, cmd, autosquash);
+               break;
+       case SKIP: {
+               struct string_list merge_rr = STRING_LIST_INIT_DUP;
+
+               rerere_clear(&merge_rr);
+               /* fallthrough */
+       case CONTINUE:
+               ret = sequencer_continue(&opts);
+               break;
+       }
+       case EDIT_TODO:
+               ret = edit_todo_list(flags);
+               break;
+       case SHOW_CURRENT_PATCH: {
+               struct child_process cmd = CHILD_PROCESS_INIT;
+
+               cmd.git_cmd = 1;
+               argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
+               ret = run_command(&cmd);
+
+               break;
+       }
+       case SHORTEN_OIDS:
+       case EXPAND_OIDS:
+               ret = transform_todos(flags);
+               break;
+       case CHECK_TODO_LIST:
+               ret = check_todo_list();
+               break;
+       case REARRANGE_SQUASH:
+               ret = rearrange_squash();
+               break;
+       case ADD_EXEC:
+               ret = sequencer_add_exec_commands(cmd);
+               break;
+       default:
+               BUG("invalid command '%d'", command);
+       }
+
+       return !!ret;
+}
diff --git a/builtin/rebase.c b/builtin/rebase.c
new file mode 100644 (file)
index 0000000..0ee06aa
--- /dev/null
@@ -0,0 +1,1545 @@
+/*
+ * "git rebase" builtin command
+ *
+ * Copyright (c) 2018 Pratik Karki
+ */
+
+#include "builtin.h"
+#include "run-command.h"
+#include "exec-cmd.h"
+#include "argv-array.h"
+#include "dir.h"
+#include "packfile.h"
+#include "refs.h"
+#include "quote.h"
+#include "config.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+#include "lockfile.h"
+#include "parse-options.h"
+#include "commit.h"
+#include "diff.h"
+#include "wt-status.h"
+#include "revision.h"
+#include "commit-reach.h"
+#include "rerere.h"
+
+static char const * const builtin_rebase_usage[] = {
+       N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
+               "[<upstream>] [<branch>]"),
+       N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
+               "--root [<branch>]"),
+       N_("git rebase --continue | --abort | --skip | --edit-todo"),
+       NULL
+};
+
+static GIT_PATH_FUNC(apply_dir, "rebase-apply")
+static GIT_PATH_FUNC(merge_dir, "rebase-merge")
+
+enum rebase_type {
+       REBASE_UNSPECIFIED = -1,
+       REBASE_AM,
+       REBASE_MERGE,
+       REBASE_INTERACTIVE,
+       REBASE_PRESERVE_MERGES
+};
+
+static int use_builtin_rebase(void)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf out = STRBUF_INIT;
+       int ret;
+
+       argv_array_pushl(&cp.args,
+                        "config", "--bool", "rebase.usebuiltin", NULL);
+       cp.git_cmd = 1;
+       if (capture_command(&cp, &out, 6)) {
+               strbuf_release(&out);
+               return 1;
+       }
+
+       strbuf_trim(&out);
+       ret = !strcmp("true", out.buf);
+       strbuf_release(&out);
+       return ret;
+}
+
+struct rebase_options {
+       enum rebase_type type;
+       const char *state_dir;
+       struct commit *upstream;
+       const char *upstream_name;
+       const char *upstream_arg;
+       char *head_name;
+       struct object_id orig_head;
+       struct commit *onto;
+       const char *onto_name;
+       const char *revisions;
+       const char *switch_to;
+       int root;
+       struct object_id *squash_onto;
+       struct commit *restrict_revision;
+       int dont_finish_rebase;
+       enum {
+               REBASE_NO_QUIET = 1<<0,
+               REBASE_VERBOSE = 1<<1,
+               REBASE_DIFFSTAT = 1<<2,
+               REBASE_FORCE = 1<<3,
+               REBASE_INTERACTIVE_EXPLICIT = 1<<4,
+       } flags;
+       struct strbuf git_am_opt;
+       const char *action;
+       int signoff;
+       int allow_rerere_autoupdate;
+       int keep_empty;
+       int autosquash;
+       char *gpg_sign_opt;
+       int autostash;
+       char *cmd;
+       int allow_empty_message;
+       int rebase_merges, rebase_cousins;
+       char *strategy, *strategy_opts;
+       struct strbuf git_format_patch_opt;
+};
+
+static int is_interactive(struct rebase_options *opts)
+{
+       return opts->type == REBASE_INTERACTIVE ||
+               opts->type == REBASE_PRESERVE_MERGES;
+}
+
+static void imply_interactive(struct rebase_options *opts, const char *option)
+{
+       switch (opts->type) {
+       case REBASE_AM:
+               die(_("%s requires an interactive rebase"), option);
+               break;
+       case REBASE_INTERACTIVE:
+       case REBASE_PRESERVE_MERGES:
+               break;
+       case REBASE_MERGE:
+               /* we silently *upgrade* --merge to --interactive if needed */
+       default:
+               opts->type = REBASE_INTERACTIVE; /* implied */
+               break;
+       }
+}
+
+/* Returns the filename prefixed by the state_dir */
+static const char *state_dir_path(const char *filename, struct rebase_options *opts)
+{
+       static struct strbuf path = STRBUF_INIT;
+       static size_t prefix_len;
+
+       if (!prefix_len) {
+               strbuf_addf(&path, "%s/", opts->state_dir);
+               prefix_len = path.len;
+       }
+
+       strbuf_setlen(&path, prefix_len);
+       strbuf_addstr(&path, filename);
+       return path.buf;
+}
+
+/* Read one file, then strip line endings */
+static int read_one(const char *path, struct strbuf *buf)
+{
+       if (strbuf_read_file(buf, path, 0) < 0)
+               return error_errno(_("could not read '%s'"), path);
+       strbuf_trim_trailing_newline(buf);
+       return 0;
+}
+
+/* Initialize the rebase options from the state directory. */
+static int read_basic_state(struct rebase_options *opts)
+{
+       struct strbuf head_name = STRBUF_INIT;
+       struct strbuf buf = STRBUF_INIT;
+       struct object_id oid;
+
+       if (read_one(state_dir_path("head-name", opts), &head_name) ||
+           read_one(state_dir_path("onto", opts), &buf))
+               return -1;
+       opts->head_name = starts_with(head_name.buf, "refs/") ?
+               xstrdup(head_name.buf) : NULL;
+       strbuf_release(&head_name);
+       if (get_oid(buf.buf, &oid))
+               return error(_("could not get 'onto': '%s'"), buf.buf);
+       opts->onto = lookup_commit_or_die(&oid, buf.buf);
+
+       /*
+        * We always write to orig-head, but interactive rebase used to write to
+        * head. Fall back to reading from head to cover for the case that the
+        * user upgraded git with an ongoing interactive rebase.
+        */
+       strbuf_reset(&buf);
+       if (file_exists(state_dir_path("orig-head", opts))) {
+               if (read_one(state_dir_path("orig-head", opts), &buf))
+                       return -1;
+       } else if (read_one(state_dir_path("head", opts), &buf))
+               return -1;
+       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)
+               opts->flags &= ~REBASE_NO_QUIET;
+       else
+               opts->flags |= REBASE_NO_QUIET;
+
+       if (file_exists(state_dir_path("verbose", opts)))
+               opts->flags |= REBASE_VERBOSE;
+
+       if (file_exists(state_dir_path("signoff", opts))) {
+               opts->signoff = 1;
+               opts->flags |= REBASE_FORCE;
+       }
+
+       if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
+               strbuf_reset(&buf);
+               if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
+                           &buf))
+                       return -1;
+               if (!strcmp(buf.buf, "--rerere-autoupdate"))
+                       opts->allow_rerere_autoupdate = 1;
+               else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
+                       opts->allow_rerere_autoupdate = 0;
+               else
+                       warning(_("ignoring invalid allow_rerere_autoupdate: "
+                                 "'%s'"), buf.buf);
+       } else
+               opts->allow_rerere_autoupdate = -1;
+
+       if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
+               strbuf_reset(&buf);
+               if (read_one(state_dir_path("gpg_sign_opt", opts),
+                           &buf))
+                       return -1;
+               free(opts->gpg_sign_opt);
+               opts->gpg_sign_opt = xstrdup(buf.buf);
+       }
+
+       if (file_exists(state_dir_path("strategy", opts))) {
+               strbuf_reset(&buf);
+               if (read_one(state_dir_path("strategy", opts), &buf))
+                       return -1;
+               free(opts->strategy);
+               opts->strategy = xstrdup(buf.buf);
+       }
+
+       if (file_exists(state_dir_path("strategy_opts", opts))) {
+               strbuf_reset(&buf);
+               if (read_one(state_dir_path("strategy_opts", opts), &buf))
+                       return -1;
+               free(opts->strategy_opts);
+               opts->strategy_opts = xstrdup(buf.buf);
+       }
+
+       strbuf_release(&buf);
+
+       return 0;
+}
+
+static int apply_autostash(struct rebase_options *opts)
+{
+       const char *path = state_dir_path("autostash", opts);
+       struct strbuf autostash = STRBUF_INIT;
+       struct child_process stash_apply = CHILD_PROCESS_INIT;
+
+       if (!file_exists(path))
+               return 0;
+
+       if (read_one(path, &autostash))
+               return error(_("Could not read '%s'"), path);
+       /* Ensure that the hash is not mistaken for a number */
+       strbuf_addstr(&autostash, "^0");
+       argv_array_pushl(&stash_apply.args,
+                        "stash", "apply", autostash.buf, NULL);
+       stash_apply.git_cmd = 1;
+       stash_apply.no_stderr = stash_apply.no_stdout =
+               stash_apply.no_stdin = 1;
+       if (!run_command(&stash_apply))
+               printf(_("Applied autostash.\n"));
+       else {
+               struct argv_array args = ARGV_ARRAY_INIT;
+               int res = 0;
+
+               argv_array_pushl(&args,
+                                "stash", "store", "-m", "autostash", "-q",
+                                autostash.buf, NULL);
+               if (run_command_v_opt(args.argv, RUN_GIT_CMD))
+                       res = error(_("Cannot store %s"), autostash.buf);
+               argv_array_clear(&args);
+               strbuf_release(&autostash);
+               if (res)
+                       return res;
+
+               fprintf(stderr,
+                       _("Applying autostash resulted in conflicts.\n"
+                         "Your changes are safe in the stash.\n"
+                         "You can run \"git stash pop\" or \"git stash drop\" "
+                         "at any time.\n"));
+       }
+
+       strbuf_release(&autostash);
+       return 0;
+}
+
+static int finish_rebase(struct rebase_options *opts)
+{
+       struct strbuf dir = STRBUF_INIT;
+       const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+
+       delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+       apply_autostash(opts);
+       close_all_packs(the_repository->objects);
+       /*
+        * We ignore errors in 'gc --auto', since the
+        * user should see them.
+        */
+       run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+       strbuf_addstr(&dir, opts->state_dir);
+       remove_dir_recursively(&dir, 0);
+       strbuf_release(&dir);
+
+       return 0;
+}
+
+static struct commit *peel_committish(const char *name)
+{
+       struct object *obj;
+       struct object_id oid;
+
+       if (get_oid(name, &oid))
+               return NULL;
+       obj = parse_object(the_repository, &oid);
+       return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+}
+
+static void add_var(struct strbuf *buf, const char *name, const char *value)
+{
+       if (!value)
+               strbuf_addf(buf, "unset %s; ", name);
+       else {
+               strbuf_addf(buf, "%s=", name);
+               sq_quote_buf(buf, value);
+               strbuf_addstr(buf, "; ");
+       }
+}
+
+static const char *resolvemsg =
+N_("Resolve all conflicts manually, mark them as resolved with\n"
+"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
+"You can instead skip this commit: run \"git rebase --skip\".\n"
+"To abort and get back to the state before \"git rebase\", run "
+"\"git rebase --abort\".");
+
+static int run_specific_rebase(struct rebase_options *opts)
+{
+       const char *argv[] = { NULL, NULL };
+       struct strbuf script_snippet = STRBUF_INIT;
+       int status;
+       const char *backend, *backend_func;
+
+       if (opts->type == REBASE_INTERACTIVE) {
+               /* Run builtin interactive rebase */
+               struct child_process child = CHILD_PROCESS_INIT;
+
+               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=:");
+                       opts->autosquash = 0;
+               }
+
+               child.git_cmd = 1;
+               argv_array_push(&child.args, "rebase--interactive");
+
+               if (opts->action)
+                       argv_array_pushf(&child.args, "--%s", opts->action);
+               if (opts->keep_empty)
+                       argv_array_push(&child.args, "--keep-empty");
+               if (opts->rebase_merges)
+                       argv_array_push(&child.args, "--rebase-merges");
+               if (opts->rebase_cousins)
+                       argv_array_push(&child.args, "--rebase-cousins");
+               if (opts->autosquash)
+                       argv_array_push(&child.args, "--autosquash");
+               if (opts->flags & REBASE_VERBOSE)
+                       argv_array_push(&child.args, "--verbose");
+               if (opts->flags & REBASE_FORCE)
+                       argv_array_push(&child.args, "--no-ff");
+               if (opts->restrict_revision)
+                       argv_array_pushf(&child.args,
+                                        "--restrict-revision=^%s",
+                                        oid_to_hex(&opts->restrict_revision->object.oid));
+               if (opts->upstream)
+                       argv_array_pushf(&child.args, "--upstream=%s",
+                                        oid_to_hex(&opts->upstream->object.oid));
+               if (opts->onto)
+                       argv_array_pushf(&child.args, "--onto=%s",
+                                        oid_to_hex(&opts->onto->object.oid));
+               if (opts->squash_onto)
+                       argv_array_pushf(&child.args, "--squash-onto=%s",
+                                        oid_to_hex(opts->squash_onto));
+               if (opts->onto_name)
+                       argv_array_pushf(&child.args, "--onto-name=%s",
+                                        opts->onto_name);
+               argv_array_pushf(&child.args, "--head-name=%s",
+                                opts->head_name ?
+                                opts->head_name : "detached HEAD");
+               if (opts->strategy)
+                       argv_array_pushf(&child.args, "--strategy=%s",
+                                        opts->strategy);
+               if (opts->strategy_opts)
+                       argv_array_pushf(&child.args, "--strategy-opts=%s",
+                                        opts->strategy_opts);
+               if (opts->switch_to)
+                       argv_array_pushf(&child.args, "--switch-to=%s",
+                                        opts->switch_to);
+               if (opts->cmd)
+                       argv_array_pushf(&child.args, "--cmd=%s", opts->cmd);
+               if (opts->allow_empty_message)
+                       argv_array_push(&child.args, "--allow-empty-message");
+               if (opts->allow_rerere_autoupdate > 0)
+                       argv_array_push(&child.args, "--rerere-autoupdate");
+               else if (opts->allow_rerere_autoupdate == 0)
+                       argv_array_push(&child.args, "--no-rerere-autoupdate");
+               if (opts->gpg_sign_opt)
+                       argv_array_push(&child.args, opts->gpg_sign_opt);
+               if (opts->signoff)
+                       argv_array_push(&child.args, "--signoff");
+
+               status = run_command(&child);
+               goto finished_rebase;
+       }
+
+       add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir()));
+       add_var(&script_snippet, "state_dir", opts->state_dir);
+
+       add_var(&script_snippet, "upstream_name", opts->upstream_name);
+       add_var(&script_snippet, "upstream", opts->upstream ?
+               oid_to_hex(&opts->upstream->object.oid) : NULL);
+       add_var(&script_snippet, "head_name",
+               opts->head_name ? opts->head_name : "detached HEAD");
+       add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head));
+       add_var(&script_snippet, "onto", opts->onto ?
+               oid_to_hex(&opts->onto->object.oid) : NULL);
+       add_var(&script_snippet, "onto_name", opts->onto_name);
+       add_var(&script_snippet, "revisions", opts->revisions);
+       add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
+               oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
+       add_var(&script_snippet, "GIT_QUIET",
+               opts->flags & REBASE_NO_QUIET ? "" : "t");
+       add_var(&script_snippet, "git_am_opt", opts->git_am_opt.buf);
+       add_var(&script_snippet, "verbose",
+               opts->flags & REBASE_VERBOSE ? "t" : "");
+       add_var(&script_snippet, "diffstat",
+               opts->flags & REBASE_DIFFSTAT ? "t" : "");
+       add_var(&script_snippet, "force_rebase",
+               opts->flags & REBASE_FORCE ? "t" : "");
+       if (opts->switch_to)
+               add_var(&script_snippet, "switch_to", opts->switch_to);
+       add_var(&script_snippet, "action", opts->action ? opts->action : "");
+       add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : "");
+       add_var(&script_snippet, "allow_rerere_autoupdate",
+               opts->allow_rerere_autoupdate < 0 ? "" :
+               opts->allow_rerere_autoupdate ?
+               "--rerere-autoupdate" : "--no-rerere-autoupdate");
+       add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
+       add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
+       add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
+       add_var(&script_snippet, "cmd", opts->cmd);
+       add_var(&script_snippet, "allow_empty_message",
+               opts->allow_empty_message ?  "--allow-empty-message" : "");
+       add_var(&script_snippet, "rebase_merges",
+               opts->rebase_merges ? "t" : "");
+       add_var(&script_snippet, "rebase_cousins",
+               opts->rebase_cousins ? "t" : "");
+       add_var(&script_snippet, "strategy", opts->strategy);
+       add_var(&script_snippet, "strategy_opts", opts->strategy_opts);
+       add_var(&script_snippet, "rebase_root", opts->root ? "t" : "");
+       add_var(&script_snippet, "squash_onto",
+               opts->squash_onto ? oid_to_hex(opts->squash_onto) : "");
+       add_var(&script_snippet, "git_format_patch_opt",
+               opts->git_format_patch_opt.buf);
+
+       if (is_interactive(opts) &&
+           !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
+               strbuf_addstr(&script_snippet,
+                             "GIT_EDITOR=:; export GIT_EDITOR; ");
+               opts->autosquash = 0;
+       }
+
+       switch (opts->type) {
+       case REBASE_AM:
+               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";
+               break;
+       default:
+               BUG("Unhandled rebase type %d", opts->type);
+               break;
+       }
+
+       strbuf_addf(&script_snippet,
+                   ". git-sh-setup && . git-rebase--common &&"
+                   " . %s && %s", backend, backend_func);
+       argv[0] = script_snippet.buf;
+
+       status = run_command_v_opt(argv, RUN_USING_SHELL);
+finished_rebase:
+       if (opts->dont_finish_rebase)
+               ; /* do nothing */
+       else if (opts->type == REBASE_INTERACTIVE)
+               ; /* interactive rebase cleans up after itself */
+       else if (status == 0) {
+               if (!file_exists(state_dir_path("stopped-sha", opts)))
+                       finish_rebase(opts);
+       } else if (status == 2) {
+               struct strbuf dir = STRBUF_INIT;
+
+               apply_autostash(opts);
+               strbuf_addstr(&dir, opts->state_dir);
+               remove_dir_recursively(&dir, 0);
+               strbuf_release(&dir);
+               die("Nothing to do");
+       }
+
+       strbuf_release(&script_snippet);
+
+       return status ? -1 : 0;
+}
+
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
+
+static int reset_head(struct object_id *oid, const char *action,
+                     const char *switch_to_branch, int detach_head,
+                     const char *reflog_orig_head, const char *reflog_head)
+{
+       struct object_id head_oid;
+       struct tree_desc desc;
+       struct lock_file lock = LOCK_INIT;
+       struct unpack_trees_options unpack_tree_opts;
+       struct tree *tree;
+       const char *reflog_action;
+       struct strbuf msg = STRBUF_INIT;
+       size_t prefix_len;
+       struct object_id *orig = NULL, oid_orig,
+               *old_orig = NULL, oid_old_orig;
+       int ret = 0;
+
+       if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
+               BUG("Not a fully qualified branch: '%s'", switch_to_branch);
+
+       if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
+               return -1;
+
+       if (!oid) {
+               if (get_oid("HEAD", &head_oid)) {
+                       rollback_lock_file(&lock);
+                       return error(_("could not determine HEAD revision"));
+               }
+               oid = &head_oid;
+       }
+
+       memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
+       setup_unpack_trees_porcelain(&unpack_tree_opts, action);
+       unpack_tree_opts.head_idx = 1;
+       unpack_tree_opts.src_index = the_repository->index;
+       unpack_tree_opts.dst_index = the_repository->index;
+       unpack_tree_opts.fn = oneway_merge;
+       unpack_tree_opts.update = 1;
+       unpack_tree_opts.merge = 1;
+       if (!detach_head)
+               unpack_tree_opts.reset = 1;
+
+       if (read_index_unmerged(the_repository->index) < 0) {
+               rollback_lock_file(&lock);
+               return error(_("could not read index"));
+       }
+
+       if (!fill_tree_descriptor(&desc, oid)) {
+               error(_("failed to find tree of %s"), oid_to_hex(oid));
+               rollback_lock_file(&lock);
+               free((void *)desc.buffer);
+               return -1;
+       }
+
+       if (unpack_trees(1, &desc, &unpack_tree_opts)) {
+               rollback_lock_file(&lock);
+               free((void *)desc.buffer);
+               return -1;
+       }
+
+       tree = parse_tree_indirect(oid);
+       prime_cache_tree(the_repository->index, tree);
+
+       if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0)
+               ret = error(_("could not write index"));
+       free((void *)desc.buffer);
+
+       if (ret)
+               return ret;
+
+       reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
+       strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
+       prefix_len = msg.len;
+
+       if (!get_oid("ORIG_HEAD", &oid_old_orig))
+               old_orig = &oid_old_orig;
+       if (!get_oid("HEAD", &oid_orig)) {
+               orig = &oid_orig;
+               if (!reflog_orig_head) {
+                       strbuf_addstr(&msg, "updating ORIG_HEAD");
+                       reflog_orig_head = msg.buf;
+               }
+               update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0,
+                          UPDATE_REFS_MSG_ON_ERR);
+       } else if (old_orig)
+               delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+       if (!reflog_head) {
+               strbuf_setlen(&msg, prefix_len);
+               strbuf_addstr(&msg, "updating HEAD");
+               reflog_head = msg.buf;
+       }
+       if (!switch_to_branch)
+               ret = update_ref(reflog_head, "HEAD", oid, orig, REF_NO_DEREF,
+                                UPDATE_REFS_MSG_ON_ERR);
+       else {
+               ret = create_symref("HEAD", switch_to_branch, msg.buf);
+               if (!ret)
+                       ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
+                                        UPDATE_REFS_MSG_ON_ERR);
+       }
+
+       strbuf_release(&msg);
+       return ret;
+}
+
+static int rebase_config(const char *var, const char *value, void *data)
+{
+       struct rebase_options *opts = data;
+
+       if (!strcmp(var, "rebase.stat")) {
+               if (git_config_bool(var, value))
+                       opts->flags |= REBASE_DIFFSTAT;
+               else
+                       opts->flags &= !REBASE_DIFFSTAT;
+               return 0;
+       }
+
+       if (!strcmp(var, "rebase.autosquash")) {
+               opts->autosquash = git_config_bool(var, value);
+               return 0;
+       }
+
+       if (!strcmp(var, "commit.gpgsign")) {
+               free(opts->gpg_sign_opt);
+               opts->gpg_sign_opt = git_config_bool(var, value) ?
+                       xstrdup("-S") : NULL;
+               return 0;
+       }
+
+       if (!strcmp(var, "rebase.autostash")) {
+               opts->autostash = git_config_bool(var, value);
+               return 0;
+       }
+
+       return git_default_config(var, value, data);
+}
+
+/*
+ * Determines whether the commits in from..to are linear, i.e. contain
+ * no merge commits. This function *expects* `from` to be an ancestor of
+ * `to`.
+ */
+static int is_linear_history(struct commit *from, struct commit *to)
+{
+       while (to && to != from) {
+               parse_commit(to);
+               if (!to->parents)
+                       return 1;
+               if (to->parents->next)
+                       return 0;
+               to = to->parents->item;
+       }
+       return 1;
+}
+
+static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
+                           struct object_id *merge_base)
+{
+       struct commit *head = lookup_commit(the_repository, head_oid);
+       struct commit_list *merge_bases;
+       int res;
+
+       if (!head)
+               return 0;
+
+       merge_bases = get_merge_bases(onto, head);
+       if (merge_bases && !merge_bases->next) {
+               oidcpy(merge_base, &merge_bases->item->object.oid);
+               res = oideq(merge_base, &onto->object.oid);
+       } else {
+               oidcpy(merge_base, &null_oid);
+               res = 0;
+       }
+       free_commit_list(merge_bases);
+       return res && is_linear_history(onto, head);
+}
+
+/* -i followed by -m is still -i */
+static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
+{
+       struct rebase_options *opts = opt->value;
+
+       if (!is_interactive(opts))
+               opts->type = REBASE_MERGE;
+
+       return 0;
+}
+
+/* -i followed by -p is still explicitly interactive, but -p alone is not */
+static int parse_opt_interactive(const struct option *opt, const char *arg,
+                                int unset)
+{
+       struct rebase_options *opts = opt->value;
+
+       opts->type = REBASE_INTERACTIVE;
+       opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
+
+       return 0;
+}
+
+static void NORETURN error_on_missing_default_upstream(void)
+{
+       struct branch *current_branch = branch_get(NULL);
+
+       printf(_("%s\n"
+                "Please specify which branch you want to rebase against.\n"
+                "See git-rebase(1) for details.\n"
+                "\n"
+                "    git rebase '<branch>'\n"
+                "\n"),
+               current_branch ? _("There is no tracking information for "
+                       "the current branch.") :
+                       _("You are not currently on a branch."));
+
+       if (current_branch) {
+               const char *remote = current_branch->remote_name;
+
+               if (!remote)
+                       remote = _("<remote>");
+
+               printf(_("If you wish to set tracking information for this "
+                        "branch you can do so with:\n"
+                        "\n"
+                        "    git branch --set-upstream-to=%s/<branch> %s\n"
+                        "\n"),
+                      remote, current_branch->name);
+       }
+       exit(1);
+}
+
+int cmd_rebase(int argc, const char **argv, const char *prefix)
+{
+       struct rebase_options options = {
+               .type = REBASE_UNSPECIFIED,
+               .flags = REBASE_NO_QUIET,
+               .git_am_opt = STRBUF_INIT,
+               .allow_rerere_autoupdate  = -1,
+               .allow_empty_message = 1,
+               .git_format_patch_opt = STRBUF_INIT,
+       };
+       const char *branch_name;
+       int ret, flags, total_argc, in_progress = 0;
+       int ok_to_skip_pre_rebase = 0;
+       struct strbuf msg = STRBUF_INIT;
+       struct strbuf revisions = STRBUF_INIT;
+       struct strbuf buf = STRBUF_INIT;
+       struct object_id merge_base;
+       enum {
+               NO_ACTION,
+               ACTION_CONTINUE,
+               ACTION_SKIP,
+               ACTION_ABORT,
+               ACTION_QUIT,
+               ACTION_EDIT_TODO,
+               ACTION_SHOW_CURRENT_PATCH,
+       } action = NO_ACTION;
+       int committer_date_is_author_date = 0;
+       int ignore_date = 0;
+       int ignore_whitespace = 0;
+       const char *gpg_sign = NULL;
+       int opt_c = -1;
+       struct string_list whitespace = STRING_LIST_INIT_NODUP;
+       struct string_list exec = STRING_LIST_INIT_NODUP;
+       const char *rebase_merges = NULL;
+       int fork_point = -1;
+       struct string_list strategy_options = STRING_LIST_INIT_NODUP;
+       struct object_id squash_onto;
+       char *squash_onto_name = NULL;
+       struct option builtin_rebase_options[] = {
+               OPT_STRING(0, "onto", &options.onto_name,
+                          N_("revision"),
+                          N_("rebase onto given branch instead of upstream")),
+               OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase,
+                        N_("allow pre-rebase hook to run")),
+               OPT_NEGBIT('q', "quiet", &options.flags,
+                          N_("be quiet. implies --no-stat"),
+                          REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT),
+               OPT_BIT('v', "verbose", &options.flags,
+                       N_("display a diffstat of what changed upstream"),
+                       REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
+               {OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL,
+                       N_("do not show diffstat of what changed upstream"),
+                       PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
+               OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace,
+                        N_("passed to 'git apply'")),
+               OPT_BOOL(0, "signoff", &options.signoff,
+                        N_("add a Signed-off-by: line to each commit")),
+               OPT_BOOL(0, "committer-date-is-author-date",
+                        &committer_date_is_author_date,
+                        N_("passed to 'git am'")),
+               OPT_BOOL(0, "ignore-date", &ignore_date,
+                        N_("passed to 'git am'")),
+               OPT_BIT('f', "force-rebase", &options.flags,
+                       N_("cherry-pick all commits, even if unchanged"),
+                       REBASE_FORCE),
+               OPT_BIT(0, "no-ff", &options.flags,
+                       N_("cherry-pick all commits, even if unchanged"),
+                       REBASE_FORCE),
+               OPT_CMDMODE(0, "continue", &action, N_("continue"),
+                           ACTION_CONTINUE),
+               OPT_CMDMODE(0, "skip", &action,
+                           N_("skip current patch and continue"), ACTION_SKIP),
+               OPT_CMDMODE(0, "abort", &action,
+                           N_("abort and check out the original branch"),
+                           ACTION_ABORT),
+               OPT_CMDMODE(0, "quit", &action,
+                           N_("abort but keep HEAD where it is"), ACTION_QUIT),
+               OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list "
+                           "during an interactive rebase"), ACTION_EDIT_TODO),
+               OPT_CMDMODE(0, "show-current-patch", &action,
+                           N_("show the patch file being applied or merged"),
+                           ACTION_SHOW_CURRENT_PATCH),
+               { OPTION_CALLBACK, 'm', "merge", &options, NULL,
+                       N_("use merging strategies to rebase"),
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       parse_opt_merge },
+               { OPTION_CALLBACK, 'i', "interactive", &options, NULL,
+                       N_("let the user edit the list of commits to rebase"),
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       parse_opt_interactive },
+               OPT_SET_INT('p', "preserve-merges", &options.type,
+                           N_("try to recreate merges instead of ignoring "
+                              "them"), REBASE_PRESERVE_MERGES),
+               OPT_BOOL(0, "rerere-autoupdate",
+                        &options.allow_rerere_autoupdate,
+                        N_("allow rerere to update index  with resolved "
+                           "conflict")),
+               OPT_BOOL('k', "keep-empty", &options.keep_empty,
+                        N_("preserve empty commits during rebase")),
+               OPT_BOOL(0, "autosquash", &options.autosquash,
+                        N_("move commits that begin with "
+                           "squash!/fixup! under -i")),
+               { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
+                       N_("GPG-sign commits"),
+                       PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+               OPT_STRING_LIST(0, "whitespace", &whitespace,
+                               N_("whitespace"), N_("passed to 'git apply'")),
+               OPT_SET_INT('C', NULL, &opt_c, N_("passed to 'git apply'"),
+                           REBASE_AM),
+               OPT_BOOL(0, "autostash", &options.autostash,
+                        N_("automatically stash/stash pop before and after")),
+               OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
+                               N_("add exec lines after each commit of the "
+                                  "editable list")),
+               OPT_BOOL(0, "allow-empty-message",
+                        &options.allow_empty_message,
+                        N_("allow rebasing commits with empty messages")),
+               {OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
+                       N_("mode"),
+                       N_("try to rebase merges instead of skipping them"),
+                       PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+               OPT_BOOL(0, "fork-point", &fork_point,
+                        N_("use 'merge-base --fork-point' to refine upstream")),
+               OPT_STRING('s', "strategy", &options.strategy,
+                          N_("strategy"), N_("use the given merge strategy")),
+               OPT_STRING_LIST('X', "strategy-option", &strategy_options,
+                               N_("option"),
+                               N_("pass the argument through to the merge "
+                                  "strategy")),
+               OPT_BOOL(0, "root", &options.root,
+                        N_("rebase all reachable commits up to the root(s)")),
+               OPT_END(),
+       };
+
+       /*
+        * NEEDSWORK: Once the builtin rebase has been tested enough
+        * and git-legacy-rebase.sh is retired to contrib/, this preamble
+        * can be removed.
+        */
+
+       if (!use_builtin_rebase()) {
+               const char *path = mkpath("%s/git-legacy-rebase",
+                                         git_exec_path());
+
+               if (sane_execvp(path, (char **)argv) < 0)
+                       die_errno(_("could not exec %s"), path);
+               else
+                       BUG("sane_execvp() returned???");
+       }
+
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(builtin_rebase_usage,
+                                  builtin_rebase_options);
+
+       prefix = setup_git_directory();
+       trace_repo_setup(prefix);
+       setup_work_tree();
+
+       git_config(rebase_config, &options);
+
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "%s/applying", apply_dir());
+       if(file_exists(buf.buf))
+               die(_("It looks like 'git am' is in progress. Cannot rebase."));
+
+       if (is_directory(apply_dir())) {
+               options.type = REBASE_AM;
+               options.state_dir = apply_dir();
+       } else if (is_directory(merge_dir())) {
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "%s/rewritten", merge_dir());
+               if (is_directory(buf.buf)) {
+                       options.type = REBASE_PRESERVE_MERGES;
+                       options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+               } else {
+                       strbuf_reset(&buf);
+                       strbuf_addf(&buf, "%s/interactive", merge_dir());
+                       if(file_exists(buf.buf)) {
+                               options.type = REBASE_INTERACTIVE;
+                               options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+                       } else
+                               options.type = REBASE_MERGE;
+               }
+               options.state_dir = merge_dir();
+       }
+
+       if (options.type != REBASE_UNSPECIFIED)
+               in_progress = 1;
+
+       total_argc = argc;
+       argc = parse_options(argc, argv, prefix,
+                            builtin_rebase_options,
+                            builtin_rebase_usage, 0);
+
+       if (action != NO_ACTION && total_argc != 2) {
+               usage_with_options(builtin_rebase_usage,
+                                  builtin_rebase_options);
+       }
+
+       if (argc > 2)
+               usage_with_options(builtin_rebase_usage,
+                                  builtin_rebase_options);
+
+       if (action != NO_ACTION && !in_progress)
+               die(_("No rebase in progress?"));
+
+       if (action == ACTION_EDIT_TODO && !is_interactive(&options))
+               die(_("The --edit-todo action can only be used during "
+                     "interactive rebase."));
+
+       switch (action) {
+       case ACTION_CONTINUE: {
+               struct object_id head;
+               struct lock_file lock_file = LOCK_INIT;
+               int fd;
+
+               options.action = "continue";
+
+               /* Sanity check */
+               if (get_oid("HEAD", &head))
+                       die(_("Cannot read HEAD"));
+
+               fd = hold_locked_index(&lock_file, 0);
+               if (read_index(the_repository->index) < 0)
+                       die(_("could not read index"));
+               refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
+                             NULL);
+               if (0 <= fd)
+                       update_index_if_able(the_repository->index,
+                                            &lock_file);
+               rollback_lock_file(&lock_file);
+
+               if (has_unstaged_changes(1)) {
+                       puts(_("You must edit all merge conflicts and then\n"
+                              "mark them as resolved using git add"));
+                       exit(1);
+               }
+               if (read_basic_state(&options))
+                       exit(1);
+               goto run_rebase;
+       }
+       case ACTION_SKIP: {
+               struct string_list merge_rr = STRING_LIST_INIT_DUP;
+
+               options.action = "skip";
+
+               rerere_clear(&merge_rr);
+               string_list_clear(&merge_rr, 1);
+
+               if (reset_head(NULL, "reset", NULL, 0, NULL, NULL) < 0)
+                       die(_("could not discard worktree changes"));
+               if (read_basic_state(&options))
+                       exit(1);
+               goto run_rebase;
+       }
+       case ACTION_ABORT: {
+               struct string_list merge_rr = STRING_LIST_INIT_DUP;
+               options.action = "abort";
+
+               rerere_clear(&merge_rr);
+               string_list_clear(&merge_rr, 1);
+
+               if (read_basic_state(&options))
+                       exit(1);
+               if (reset_head(&options.orig_head, "reset",
+                              options.head_name, 0, NULL, NULL) < 0)
+                       die(_("could not move back to %s"),
+                           oid_to_hex(&options.orig_head));
+               ret = finish_rebase(&options);
+               goto cleanup;
+       }
+       case ACTION_QUIT: {
+               strbuf_reset(&buf);
+               strbuf_addstr(&buf, options.state_dir);
+               ret = !!remove_dir_recursively(&buf, 0);
+               if (ret)
+                       die(_("could not remove '%s'"), options.state_dir);
+               goto cleanup;
+       }
+       case ACTION_EDIT_TODO:
+               options.action = "edit-todo";
+               options.dont_finish_rebase = 1;
+               goto run_rebase;
+       case ACTION_SHOW_CURRENT_PATCH:
+               options.action = "show-current-patch";
+               options.dont_finish_rebase = 1;
+               goto run_rebase;
+       case NO_ACTION:
+               break;
+       default:
+               BUG("action: %d", action);
+       }
+
+       /* Make sure no rebase is in progress */
+       if (in_progress) {
+               const char *last_slash = strrchr(options.state_dir, '/');
+               const char *state_dir_base =
+                       last_slash ? last_slash + 1 : options.state_dir;
+               const char *cmd_live_rebase =
+                       "git rebase (--continue | --abort | --skip)";
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "rm -fr \"%s\"", options.state_dir);
+               die(_("It seems that there is already a %s directory, and\n"
+                     "I wonder if you are in the middle of another rebase.  "
+                     "If that is the\n"
+                     "case, please try\n\t%s\n"
+                     "If that is not the case, please\n\t%s\n"
+                     "and run me again.  I am stopping in case you still "
+                     "have something\n"
+                     "valuable there.\n"),
+                   state_dir_base, cmd_live_rebase, buf.buf);
+       }
+
+       if (!(options.flags & REBASE_NO_QUIET))
+               strbuf_addstr(&options.git_am_opt, " -q");
+
+       if (committer_date_is_author_date) {
+               strbuf_addstr(&options.git_am_opt,
+                             " --committer-date-is-author-date");
+               options.flags |= REBASE_FORCE;
+       }
+
+       if (ignore_whitespace)
+               strbuf_addstr(&options.git_am_opt, " --ignore-whitespace");
+
+       if (ignore_date) {
+               strbuf_addstr(&options.git_am_opt, " --ignore-date");
+               options.flags |= REBASE_FORCE;
+       }
+
+       if (options.keep_empty)
+               imply_interactive(&options, "--keep-empty");
+
+       if (gpg_sign) {
+               free(options.gpg_sign_opt);
+               options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
+       }
+
+       if (opt_c >= 0)
+               strbuf_addf(&options.git_am_opt, " -C%d", opt_c);
+
+       if (whitespace.nr) {
+               int i;
+
+               for (i = 0; i < whitespace.nr; i++) {
+                       const char *item = whitespace.items[i].string;
+
+                       strbuf_addf(&options.git_am_opt, " --whitespace=%s",
+                                   item);
+
+                       if ((!strcmp(item, "fix")) || (!strcmp(item, "strip")))
+                               options.flags |= REBASE_FORCE;
+               }
+       }
+
+       if (exec.nr) {
+               int i;
+
+               imply_interactive(&options, "--exec");
+
+               strbuf_reset(&buf);
+               for (i = 0; i < exec.nr; i++)
+                       strbuf_addf(&buf, "exec %s\n", exec.items[i].string);
+               options.cmd = xstrdup(buf.buf);
+       }
+
+       if (rebase_merges) {
+               if (!*rebase_merges)
+                       ; /* default mode; do nothing */
+               else if (!strcmp("rebase-cousins", rebase_merges))
+                       options.rebase_cousins = 1;
+               else if (strcmp("no-rebase-cousins", rebase_merges))
+                       die(_("Unknown mode: %s"), rebase_merges);
+               options.rebase_merges = 1;
+               imply_interactive(&options, "--rebase-merges");
+       }
+
+       if (strategy_options.nr) {
+               int i;
+
+               if (!options.strategy)
+                       options.strategy = "recursive";
+
+               strbuf_reset(&buf);
+               for (i = 0; i < strategy_options.nr; i++)
+                       strbuf_addf(&buf, " --%s",
+                                   strategy_options.items[i].string);
+               options.strategy_opts = xstrdup(buf.buf);
+       }
+
+       if (options.strategy) {
+               options.strategy = xstrdup(options.strategy);
+               switch (options.type) {
+               case REBASE_AM:
+                       die(_("--strategy requires --merge or --interactive"));
+               case REBASE_MERGE:
+               case REBASE_INTERACTIVE:
+               case REBASE_PRESERVE_MERGES:
+                       /* compatible */
+                       break;
+               case REBASE_UNSPECIFIED:
+                       options.type = REBASE_MERGE;
+                       break;
+               default:
+                       BUG("unhandled rebase type (%d)", options.type);
+               }
+       }
+
+       if (options.root && !options.onto_name)
+               imply_interactive(&options, "--root without --onto");
+
+       if (isatty(2) && options.flags & REBASE_NO_QUIET)
+               strbuf_addstr(&options.git_format_patch_opt, " --progress");
+
+       switch (options.type) {
+       case REBASE_MERGE:
+       case REBASE_INTERACTIVE:
+       case REBASE_PRESERVE_MERGES:
+               options.state_dir = merge_dir();
+               break;
+       case REBASE_AM:
+               options.state_dir = apply_dir();
+               break;
+       default:
+               /* the default rebase backend is `--am` */
+               options.type = REBASE_AM;
+               options.state_dir = apply_dir();
+               break;
+       }
+
+       if (options.git_am_opt.len) {
+               const char *p;
+
+               /* all am options except -q are compatible only with --am */
+               strbuf_reset(&buf);
+               strbuf_addbuf(&buf, &options.git_am_opt);
+               strbuf_addch(&buf, ' ');
+               while ((p = strstr(buf.buf, " -q ")))
+                       strbuf_splice(&buf, p - buf.buf, 4, " ", 1);
+               strbuf_trim(&buf);
+
+               if (is_interactive(&options) && buf.len)
+                       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 && buf.len)
+                       die(_("error: cannot combine merge options (--merge, "
+                             "--strategy, --strategy-option) with am options "
+                             "(%s)"), buf.buf);
+       }
+
+       if (options.signoff) {
+               if (options.type == REBASE_PRESERVE_MERGES)
+                       die("cannot combine '--signoff' with "
+                           "'--preserve-merges'");
+               strbuf_addstr(&options.git_am_opt, " --signoff");
+               options.flags |= REBASE_FORCE;
+       }
+
+       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 "
+                             "'--rebase-merges'"));
+
+       if (options.rebase_merges) {
+               if (strategy_options.nr)
+                       die(_("error: cannot combine '--rebase-merges' with "
+                             "'--strategy-option'"));
+               if (options.strategy)
+                       die(_("error: cannot combine '--rebase-merges' with "
+                             "'--strategy'"));
+       }
+
+       if (!options.root) {
+               if (argc < 1) {
+                       struct branch *branch;
+
+                       branch = branch_get(NULL);
+                       options.upstream_name = branch_get_upstream(branch,
+                                                                   NULL);
+                       if (!options.upstream_name)
+                               error_on_missing_default_upstream();
+                       if (fork_point < 0)
+                               fork_point = 1;
+               } else {
+                       options.upstream_name = argv[0];
+                       argc--;
+                       argv++;
+                       if (!strcmp(options.upstream_name, "-"))
+                               options.upstream_name = "@{-1}";
+               }
+               options.upstream = peel_committish(options.upstream_name);
+               if (!options.upstream)
+                       die(_("invalid upstream '%s'"), options.upstream_name);
+               options.upstream_arg = options.upstream_name;
+       } else {
+               if (!options.onto_name) {
+                       if (commit_tree("", 0, the_hash_algo->empty_tree, NULL,
+                                       &squash_onto, NULL, NULL) < 0)
+                               die(_("Could not create new root commit"));
+                       options.squash_onto = &squash_onto;
+                       options.onto_name = squash_onto_name =
+                               xstrdup(oid_to_hex(&squash_onto));
+               }
+               options.upstream_name = NULL;
+               options.upstream = NULL;
+               if (argc > 1)
+                       usage_with_options(builtin_rebase_usage,
+                                          builtin_rebase_options);
+               options.upstream_arg = "--root";
+       }
+
+       /* Make sure the branch to rebase onto is valid. */
+       if (!options.onto_name)
+               options.onto_name = options.upstream_name;
+       if (strstr(options.onto_name, "...")) {
+               if (get_oid_mb(options.onto_name, &merge_base) < 0)
+                       die(_("'%s': need exactly one merge base"),
+                           options.onto_name);
+               options.onto = lookup_commit_or_die(&merge_base,
+                                                   options.onto_name);
+       } else {
+               options.onto = peel_committish(options.onto_name);
+               if (!options.onto)
+                       die(_("Does not point to a valid commit '%s'"),
+                               options.onto_name);
+       }
+
+       /*
+        * If the branch to rebase is given, that is the branch we will rebase
+        * branch_name -- branch/commit being rebased, or
+        *                HEAD (already detached)
+        * orig_head -- commit object name of tip of the branch before rebasing
+        * head_name -- refs/heads/<that-branch> or NULL (detached HEAD)
+        */
+       if (argc == 1) {
+               /* Is it "rebase other branchname" or "rebase other commit"? */
+               branch_name = argv[0];
+               options.switch_to = argv[0];
+
+               /* Is it a local branch? */
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "refs/heads/%s", branch_name);
+               if (!read_ref(buf.buf, &options.orig_head))
+                       options.head_name = xstrdup(buf.buf);
+               /* If not is it a valid ref (branch or commit)? */
+               else if (!get_oid(branch_name, &options.orig_head))
+                       options.head_name = NULL;
+               else
+                       die(_("fatal: no such branch/commit '%s'"),
+                           branch_name);
+       } else if (argc == 0) {
+               /* Do not need to switch branches, we are already on it. */
+               options.head_name =
+                       xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
+                                        &flags));
+               if (!options.head_name)
+                       die(_("No such ref: %s"), "HEAD");
+               if (flags & REF_ISSYMREF) {
+                       if (!skip_prefix(options.head_name,
+                                        "refs/heads/", &branch_name))
+                               branch_name = options.head_name;
+
+               } else {
+                       free(options.head_name);
+                       options.head_name = NULL;
+                       branch_name = "HEAD";
+               }
+               if (get_oid("HEAD", &options.orig_head))
+                       die(_("Could not resolve HEAD to a revision"));
+       } else
+               BUG("unexpected number of arguments left to parse");
+
+       if (fork_point > 0) {
+               struct commit *head =
+                       lookup_commit_reference(the_repository,
+                                               &options.orig_head);
+               options.restrict_revision =
+                       get_fork_point(options.upstream_name, head);
+       }
+
+       if (read_index(the_repository->index) < 0)
+               die(_("could not read index"));
+
+       if (options.autostash) {
+               struct lock_file lock_file = LOCK_INIT;
+               int fd;
+
+               fd = hold_locked_index(&lock_file, 0);
+               refresh_cache(REFRESH_QUIET);
+               if (0 <= fd)
+                       update_index_if_able(&the_index, &lock_file);
+               rollback_lock_file(&lock_file);
+
+               if (has_unstaged_changes(1) || has_uncommitted_changes(1)) {
+                       const char *autostash =
+                               state_dir_path("autostash", &options);
+                       struct child_process stash = CHILD_PROCESS_INIT;
+                       struct object_id oid;
+                       struct commit *head =
+                               lookup_commit_reference(the_repository,
+                                                       &options.orig_head);
+
+                       argv_array_pushl(&stash.args,
+                                        "stash", "create", "autostash", NULL);
+                       stash.git_cmd = 1;
+                       stash.no_stdin = 1;
+                       strbuf_reset(&buf);
+                       if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+                               die(_("Cannot autostash"));
+                       strbuf_trim_trailing_newline(&buf);
+                       if (get_oid(buf.buf, &oid))
+                               die(_("Unexpected stash response: '%s'"),
+                                   buf.buf);
+                       strbuf_reset(&buf);
+                       strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+                       if (safe_create_leading_directories_const(autostash))
+                               die(_("Could not create directory for '%s'"),
+                                   options.state_dir);
+                       write_file(autostash, "%s", oid_to_hex(&oid));
+                       printf(_("Created autostash: %s\n"), buf.buf);
+                       if (reset_head(&head->object.oid, "reset --hard",
+                                      NULL, 0, NULL, NULL) < 0)
+                               die(_("could not reset --hard"));
+                       printf(_("HEAD is now at %s"),
+                              find_unique_abbrev(&head->object.oid,
+                                                 DEFAULT_ABBREV));
+                       strbuf_reset(&buf);
+                       pp_commit_easy(CMIT_FMT_ONELINE, head, &buf);
+                       if (buf.len > 0)
+                               printf(" %s", buf.buf);
+                       putchar('\n');
+
+                       if (discard_index(the_repository->index) < 0 ||
+                               read_index(the_repository->index) < 0)
+                               die(_("could not read index"));
+               }
+       }
+
+       if (require_clean_work_tree("rebase",
+                                   _("Please commit or stash them."), 1, 1)) {
+               ret = 1;
+               goto cleanup;
+       }
+
+       /*
+        * Now we are rebasing commits upstream..orig_head (or with --root,
+        * everything leading up to orig_head) on top of onto.
+        */
+
+       /*
+        * Check if we are already based on onto with linear history,
+        * but this should be done only when upstream and onto are the same
+        * and if this is not an interactive rebase.
+        */
+       if (can_fast_forward(options.onto, &options.orig_head, &merge_base) &&
+           !is_interactive(&options) && !options.restrict_revision &&
+           options.upstream &&
+           !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) {
+               int flag;
+
+               if (!(options.flags & REBASE_FORCE)) {
+                       /* Lazily switch to the target branch if needed... */
+                       if (options.switch_to) {
+                               struct object_id oid;
+
+                               if (get_oid(options.switch_to, &oid) < 0) {
+                                       ret = !!error(_("could not parse '%s'"),
+                                                     options.switch_to);
+                                       goto cleanup;
+                               }
+
+                               strbuf_reset(&buf);
+                               strbuf_addf(&buf, "rebase: checkout %s",
+                                           options.switch_to);
+                               if (reset_head(&oid, "checkout",
+                                              options.head_name, 0,
+                                              NULL, NULL) < 0) {
+                                       ret = !!error(_("could not switch to "
+                                                       "%s"),
+                                                     options.switch_to);
+                                       goto cleanup;
+                               }
+                       }
+
+                       if (!(options.flags & REBASE_NO_QUIET))
+                               ; /* be quiet */
+                       else if (!strcmp(branch_name, "HEAD") &&
+                                resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+                               puts(_("HEAD is up to date."));
+                       else
+                               printf(_("Current branch %s is up to date.\n"),
+                                      branch_name);
+                       ret = !!finish_rebase(&options);
+                       goto cleanup;
+               } else if (!(options.flags & REBASE_NO_QUIET))
+                       ; /* be quiet */
+               else if (!strcmp(branch_name, "HEAD") &&
+                        resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+                       puts(_("HEAD is up to date, rebase forced."));
+               else
+                       printf(_("Current branch %s is up to date, rebase "
+                                "forced.\n"), branch_name);
+       }
+
+       /* If a hook exists, give it a chance to interrupt*/
+       if (!ok_to_skip_pre_rebase &&
+           run_hook_le(NULL, "pre-rebase", options.upstream_arg,
+                       argc ? argv[0] : NULL, NULL))
+               die(_("The pre-rebase hook refused to rebase."));
+
+       if (options.flags & REBASE_DIFFSTAT) {
+               struct diff_options opts;
+
+               if (options.flags & REBASE_VERBOSE)
+                       printf(_("Changes from %s to %s:\n"),
+                               oid_to_hex(&merge_base),
+                               oid_to_hex(&options.onto->object.oid));
+
+               /* We want color (if set), but no pager */
+               diff_setup(&opts);
+               opts.stat_width = -1; /* use full terminal width */
+               opts.stat_graph_width = -1; /* respect statGraphWidth config */
+               opts.output_format |=
+                       DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+               opts.detect_rename = DIFF_DETECT_RENAME;
+               diff_setup_done(&opts);
+               diff_tree_oid(&merge_base, &options.onto->object.oid,
+                             "", &opts);
+               diffcore_std(&opts);
+               diff_flush(&opts);
+       }
+
+       if (is_interactive(&options))
+               goto run_rebase;
+
+       /* Detach HEAD and reset the tree */
+       if (options.flags & REBASE_NO_QUIET)
+               printf(_("First, rewinding head to replay your work on top of "
+                        "it...\n"));
+
+       strbuf_addf(&msg, "rebase: checkout %s", options.onto_name);
+       if (reset_head(&options.onto->object.oid, "checkout", NULL, 1,
+           NULL, msg.buf))
+               die(_("Could not detach HEAD"));
+       strbuf_release(&msg);
+
+       /*
+        * If the onto is a proper descendant of the tip of the branch, then
+        * we just fast-forwarded.
+        */
+       strbuf_reset(&msg);
+       if (!oidcmp(&merge_base, &options.orig_head)) {
+               printf(_("Fast-forwarded %s to %s. \n"),
+                       branch_name, options.onto_name);
+               strbuf_addf(&msg, "rebase finished: %s onto %s",
+                       options.head_name ? options.head_name : "detached HEAD",
+                       oid_to_hex(&options.onto->object.oid));
+               reset_head(NULL, "Fast-forwarded", options.head_name, 0,
+                          "HEAD", msg.buf);
+               strbuf_release(&msg);
+               ret = !!finish_rebase(&options);
+               goto cleanup;
+       }
+
+       strbuf_addf(&revisions, "%s..%s",
+                   options.root ? oid_to_hex(&options.onto->object.oid) :
+                   (options.restrict_revision ?
+                    oid_to_hex(&options.restrict_revision->object.oid) :
+                    oid_to_hex(&options.upstream->object.oid)),
+                   oid_to_hex(&options.orig_head));
+
+       options.revisions = revisions.buf;
+
+run_rebase:
+       ret = !!run_specific_rebase(&options);
+
+cleanup:
+       strbuf_release(&revisions);
+       free(options.head_name);
+       free(options.gpg_sign_opt);
+       free(options.cmd);
+       free(squash_onto_name);
+       return ret;
+}
index 95740f4f0e71de95484edf53a2b86dc71952c5a7..33187bd8e90252c283b7154bc7026e01d4e754ef 100644 (file)
@@ -281,8 +281,7 @@ static int show_ref_cb(const char *path_full, const struct object_id *oid,
        return 0;
 }
 
-static void show_one_alternate_ref(const char *refname,
-                                  const struct object_id *oid,
+static void show_one_alternate_ref(const struct object_id *oid,
                                   void *data)
 {
        struct oidset *seen = data;
@@ -1026,6 +1025,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
        const char *ret;
        struct object_id *old_oid = &cmd->old_oid;
        struct object_id *new_oid = &cmd->new_oid;
+       int do_update_worktree = 0;
 
        /* only refs/... are allowed */
        if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
@@ -1051,9 +1051,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                                refuse_unconfigured_deny();
                        return "branch is currently checked out";
                case DENY_UPDATE_INSTEAD:
-                       ret = update_worktree(new_oid->hash);
-                       if (ret)
-                               return ret;
+                       /* pass -- let other checks intervene first */
+                       do_update_worktree = 1;
                        break;
                }
        }
@@ -1118,6 +1117,12 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                return "hook declined";
        }
 
+       if (do_update_worktree) {
+               ret = update_worktree(new_oid->hash);
+               if (ret)
+                       return ret;
+       }
+
        if (is_null_oid(new_oid)) {
                struct strbuf err = STRBUF_INIT;
                if (!parse_object(the_repository, old_oid)) {
index 3acef5a0abed400f543da673dbcaa2f648bd4bef..b5941c1ff325b981593f0e54fd7f32f956696d8d 100644 (file)
@@ -567,7 +567,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
         * from reflog if the repository was pruned with older git.
         */
        if (cb.cmd.stalefix) {
-               init_revisions(&cb.cmd.revs, prefix);
+               repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
                if (flags & EXPIRE_REFLOGS_VERBOSE)
                        printf("Marking reachable objects...");
                mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
index c6a7943d5cb108dbccdfd502d799ab71a9e7e146..82c19b755509f83bf189dd70323a678c299c6b66 100644 (file)
@@ -235,8 +235,8 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
        while (strbuf_getline_lf(&line, out) != EOF) {
                char *promisor_name;
                int fd;
-               if (line.len != 40)
-                       die("repack: Expecting 40 character sha1 lines only from pack-objects.");
+               if (line.len != the_hash_algo->hexsz)
+                       die("repack: Expecting full hex object ID lines only from pack-objects.");
                string_list_append(names, line.buf);
 
                /*
@@ -407,8 +407,8 @@ 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 != 40)
-                       die("repack: Expecting 40 character sha1 lines only from pack-objects.");
+               if (line.len != the_hash_algo->hexsz)
+                       die("repack: Expecting full hex object ID lines only from pack-objects.");
                string_list_append(&names, line.buf);
        }
        fclose(out);
@@ -535,20 +535,27 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        reprepare_packed_git(the_repository);
 
        if (delete_redundant) {
+               const int hexsz = the_hash_algo->hexsz;
                int opts = 0;
                string_list_sort(&names);
                for_each_string_list_item(item, &existing_packs) {
                        char *sha1;
                        size_t len = strlen(item->string);
-                       if (len < 40)
+                       if (len < hexsz)
                                continue;
-                       sha1 = item->string + len - 40;
+                       sha1 = item->string + len - hexsz;
                        if (!string_list_has_string(&names, sha1))
                                remove_redundant_pack(packdir, item->string);
                }
                if (!po_args.quiet && isatty(2))
                        opts |= PRUNE_PACKED_VERBOSE;
                prune_packed_objects(opts);
+
+               if (!keep_unreachable &&
+                   (!(pack_everything & LOOSEN_UNREACHABLE) ||
+                    unpack_unreachable) &&
+                   is_repository_shallow(the_repository))
+                       prune_shallow(PRUNE_QUICK);
        }
 
        if (!no_update_server_info)
index 30a661ea0c71f058cdb8bc7626334cb03747fa7c..a58b9c6d130568602ac7928bd1da4a83724ff4f1 100644 (file)
@@ -295,7 +295,7 @@ static int import_object(struct object_id *oid, enum object_type type,
                        close(fd);
                        return -1;
                }
-               if (index_fd(oid, fd, &st, type, NULL, flags) < 0)
+               if (index_fd(&the_index, oid, fd, &st, type, NULL, flags) < 0)
                        return error(_("unable to write object to database"));
                /* index_fd close()s fd for us */
        }
index 5ed941b91f2c1498a3f28f0c6415437f79133432..e89ccbc524a62d5a8760311a2797dfa49fe8525d 100644 (file)
@@ -70,7 +70,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
                flags = RERERE_NOAUTOUPDATE;
 
        if (argc < 1)
-               return rerere(flags);
+               return repo_rerere(the_repository, flags);
 
        if (!strcmp(argv[0], "forget")) {
                struct pathspec pathspec;
@@ -78,7 +78,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
                        warning(_("'git rerere forget' without paths is deprecated"));
                parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD,
                               prefix, argv + 1);
-               return rerere_forget(&pathspec);
+               return rerere_forget(the_repository, &pathspec);
        }
 
        if (!strcmp(argv[0], "clear")) {
@@ -91,7 +91,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
                for (i = 0; i < merge_rr.nr; i++)
                        printf("%s\n", merge_rr.items[i].string);
        } else if (!strcmp(argv[0], "remaining")) {
-               rerere_remaining(&merge_rr);
+               rerere_remaining(the_repository, &merge_rr);
                for (i = 0; i < merge_rr.nr; i++) {
                        if (merge_rr.items[i].util != RERERE_RESOLVED)
                                printf("%s\n", merge_rr.items[i].string);
index 11cd0dcb8cc73ac753b7ed746e194ba1458742ee..6d37a35e2e583c4508b7e506e5307699ff166fb5 100644 (file)
@@ -159,6 +159,7 @@ static int read_from_tree(const struct pathspec *pathspec,
        opt.format_callback = update_index_from_diff;
        opt.format_callback_data = &intent_to_add;
        opt.flags.override_submodule_config = 1;
+       opt.repo = the_repository;
 
        if (do_diff_cache(tree_oid, &opt))
                return 1;
index ed0ea7dc5b5bed0c3cb926725b2f70cbf9b93d35..5064d08e1b8ad04544a76d1f0496134e2c15079c 100644 (file)
@@ -6,6 +6,7 @@
 #include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
+#include "object.h"
 #include "object-store.h"
 #include "pack.h"
 #include "pack-bitmap.h"
@@ -209,7 +210,8 @@ static inline void finish_object__ma(struct object *obj)
         */
        switch (arg_missing_action) {
        case MA_ERROR:
-               die("missing blob object '%s'", oid_to_hex(&obj->oid));
+               die("missing %s object '%s'",
+                   type_name(obj->type), oid_to_hex(&obj->oid));
                return;
 
        case MA_ALLOW_ANY:
@@ -222,8 +224,8 @@ static inline void finish_object__ma(struct object *obj)
        case MA_ALLOW_PROMISOR:
                if (is_promisor_object(&obj->oid))
                        return;
-               die("unexpected missing blob object '%s'",
-                   oid_to_hex(&obj->oid));
+               die("unexpected missing %s object '%s'",
+                   type_name(obj->type), oid_to_hex(&obj->oid));
                return;
 
        default:
@@ -235,7 +237,7 @@ static inline void finish_object__ma(struct object *obj)
 static int finish_object(struct object *obj, const char *name, void *cb_data)
 {
        struct rev_list_info *info = cb_data;
-       if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) {
+       if (!has_object_file(&obj->oid)) {
                finish_object__ma(obj);
                return 1;
        }
@@ -370,9 +372,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                usage(rev_list_usage);
 
        git_config(git_default_config, NULL);
-       init_revisions(&revs, prefix);
+       repo_init_revisions(the_repository, &revs, prefix);
        revs.abbrev = DEFAULT_ABBREV;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
+       revs.do_not_die_on_missing_tree = 1;
 
        /*
         * Scan the argument list before invoking setup_revisions(), so that we
index 9a66720cfce95f357dbd1564a81f51b2f67a984b..c93393c89be0290ebd9e300046ff56a2ee441715 100644 (file)
@@ -174,7 +174,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
        } else {
                struct setup_revision_opt s_r_opt;
                opts->revs = xmalloc(sizeof(*opts->revs));
-               init_revisions(opts->revs, NULL);
+               repo_init_revisions(the_repository, opts->revs, NULL);
                opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
                if (argc < 2)
                        usage_with_options(usage_str, options);
index 3898a2c9c428864caf43b0f8160a441488643e29..88f88e97b28c24f74aad6ec6cf1c637003f41a85 100644 (file)
@@ -278,7 +278,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
        shortlog_init(&log);
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        parse_options_start(&ctx, argc, argv, prefix, options,
                            PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
 
index 247881189fde2a910bbd993342bb74fe10e06b0e..676175b9befa23c70e56e12c00746b8faf899b8c 100644 (file)
@@ -584,6 +584,26 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static char *compute_submodule_clone_url(const char *rel_url)
+{
+       char *remoteurl, *relurl;
+       char *remote = get_default_remote();
+       struct strbuf remotesb = STRBUF_INIT;
+
+       strbuf_addf(&remotesb, "remote.%s.url", remote);
+       if (git_config_get_string(remotesb.buf, &remoteurl)) {
+               warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
+               remoteurl = xgetcwd();
+       }
+       relurl = relative_url(remoteurl, rel_url, NULL);
+
+       free(remote);
+       free(remoteurl);
+       strbuf_release(&remotesb);
+
+       return relurl;
+}
+
 struct init_cb {
        const char *prefix;
        unsigned int flags;
@@ -634,21 +654,9 @@ static void init_submodule(const char *path, const char *prefix,
                /* Possibly a url relative to parent */
                if (starts_with_dot_dot_slash(url) ||
                    starts_with_dot_slash(url)) {
-                       char *remoteurl, *relurl;
-                       char *remote = get_default_remote();
-                       struct strbuf remotesb = STRBUF_INIT;
-                       strbuf_addf(&remotesb, "remote.%s.url", remote);
-                       free(remote);
-
-                       if (git_config_get_string(remotesb.buf, &remoteurl)) {
-                               warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
-                               remoteurl = xgetcwd();
-                       }
-                       relurl = relative_url(remoteurl, url, NULL);
-                       strbuf_release(&remotesb);
-                       free(remoteurl);
-                       free(url);
-                       url = relurl;
+                       char *oldurl = url;
+                       url = compute_submodule_clone_url(oldurl);
+                       free(oldurl);
                }
 
                if (git_config_set_gently(sb.buf, url))
@@ -792,7 +800,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
                         path, NULL);
 
        git_config(git_diff_basic_config, NULL);
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        rev.abbrev = 0;
        diff_files_args.argc = setup_revisions(diff_files_args.argc,
                                               diff_files_args.argv,
@@ -1457,7 +1465,6 @@ static void determine_submodule_update_strategy(struct repository *r,
        key = xstrfmt("submodule.%s.update", sub->name);
 
        if (update) {
-               trace_printf("parsing update");
                if (parse_submodule_update_strategy(update, out) < 0)
                        die(_("Invalid update mode '%s' for submodule path '%s'"),
                                update, path);
@@ -1466,7 +1473,6 @@ static void determine_submodule_update_strategy(struct repository *r,
                        die(_("Invalid update mode '%s' configured for submodule path '%s'"),
                                val, path);
        } else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
-               trace_printf("loaded thing");
                out->type = sub->update_strategy.type;
                out->command = sub->update_strategy.command;
        } else
@@ -1584,6 +1590,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
        struct strbuf sb = STRBUF_INIT;
        const char *displaypath = NULL;
        int needs_cloning = 0;
+       int need_free_url = 0;
 
        if (ce_stage(ce)) {
                if (suc->recursive_prefix)
@@ -1632,8 +1639,14 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 
        strbuf_reset(&sb);
        strbuf_addf(&sb, "submodule.%s.url", sub->name);
-       if (repo_config_get_string_const(the_repository, sb.buf, &url))
-               url = sub->url;
+       if (repo_config_get_string_const(the_repository, sb.buf, &url)) {
+               if (starts_with_dot_slash(sub->url) ||
+                   starts_with_dot_dot_slash(sub->url)) {
+                       url = compute_submodule_clone_url(sub->url);
+                       need_free_url = 1;
+               } else
+                       url = sub->url;
+       }
 
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/.git", ce->name);
@@ -1679,6 +1692,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 cleanup:
        strbuf_reset(&displaypath_sb);
        strbuf_reset(&sb);
+       if (need_free_url)
+               free((void*)url);
 
        return needs_cloning;
 }
index e7fab78b3b9cf2433342e195a206d8ff3087f9b9..07c10bcb7d82c19becf424f9ab63954c9e58f93f 100644 (file)
@@ -282,7 +282,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
        fill_stat_cache_info(ce, st);
        ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
-       if (index_path(&ce->oid, path, st,
+       if (index_path(&the_index, &ce->oid, path, st,
                       info_only ? 0 : HASH_WRITE_OBJECT)) {
                discard_cache_entry(ce);
                return -1;
index 25d91163563614110d2c93802ce3fef6d5844325..018879737aeedc245a1473247df16ed0dcd26e50 100644 (file)
@@ -28,6 +28,8 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
        if (!enter_repo(argv[1], 0))
                die("'%s' does not appear to be a git repository", argv[1]);
 
+       init_archivers();
+
        /* put received options in sent_argv[] */
        argv_array_push(&sent_argv, "git-upload-archive");
        for (;;) {
index 14f2cfc24836b4a526df935e83d8e5899055f483..1ef584b93b87491dca873429f1fbbbadf19622d4 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -140,7 +140,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
        int i, ret = 0, req_nr;
        const char *message = _("Repository lacks these prerequisite commits:");
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &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);
@@ -441,7 +441,7 @@ int create_bundle(struct bundle_header *header, const char *path,
 
        /* init revs to list objects for pack-objects later */
        save_commit_buffer = 0;
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
 
        /* write prerequisites */
        if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv))
index 9c5cf2cc4fdc1f2f4ccc7f352289fe0ec75e8f5d..9d454d24bca62dcdb8c941a64d09b7a53556323a 100644 (file)
@@ -326,6 +326,7 @@ static int update_one(struct cache_tree *it,
                unsigned mode;
                int expected_missing = 0;
                int contains_ita = 0;
+               int ce_missing_ok;
 
                path = ce->name;
                pathlen = ce_namelen(ce);
@@ -355,8 +356,11 @@ static int update_one(struct cache_tree *it,
                        i++;
                }
 
+               ce_missing_ok = mode == S_IFGITLINK || missing_ok ||
+                       (repository_format_partial_clone &&
+                        ce_skip_worktree(ce));
                if (is_null_oid(oid) ||
-                   (mode != S_IFGITLINK && !missing_ok && !has_object_file(oid))) {
+                   (!ce_missing_ok && !has_object_file(oid))) {
                        strbuf_release(&buffer);
                        if (expected_missing)
                                return -1;
diff --git a/cache.h b/cache.h
index d508f3d4f8837caef469389c71950ee96eea709b..8b1ee42ae93b4bfd025f0f761e6adeac6c642907 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -410,7 +410,7 @@ void validate_cache_entries(const struct index_state *istate);
 
 #define read_cache() read_index(&the_index)
 #define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
-#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
+#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec), 0)
 #define is_cache_unborn() is_index_unborn(&the_index)
 #define read_cache_unmerged() read_index_unmerged(&the_index)
 #define discard_cache() discard_index(&the_index)
@@ -659,7 +659,9 @@ extern int daemonize(void);
 /* Initialize and use the cache information */
 struct lock_file;
 extern int read_index(struct index_state *);
-extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
+extern int read_index_preload(struct index_state *,
+                             const struct pathspec *pathspec,
+                             unsigned int refresh_flags);
 extern int do_read_index(struct index_state *istate, const char *path,
                         int must_exist); /* for testting only! */
 extern int read_index_from(struct index_state *, const char *path,
@@ -703,7 +705,7 @@ extern int unmerged_index(const struct index_state *);
  * provided, the space-separated list of files that differ will be appended
  * to it.
  */
-extern int index_has_changes(const struct index_state *istate,
+extern int index_has_changes(struct index_state *istate,
                             struct tree *tree,
                             struct strbuf *sb);
 
@@ -781,14 +783,16 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,
 #define CE_MATCH_REFRESH               0x10
 /* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
 #define CE_MATCH_IGNORE_FSMONITOR 0X20
+extern int is_racy_timestamp(const struct index_state *istate,
+                            const struct cache_entry *ce);
 extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 
 #define HASH_WRITE_OBJECT 1
 #define HASH_FORMAT_CHECK 2
 #define HASH_RENORMALIZE  4
-extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
-extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
+extern int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
+extern int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
 
 /*
  * Record to sd the data from st that we use to check whether a file
@@ -814,6 +818,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 #define REFRESH_IGNORE_MISSING 0x0008  /* ignore non-existent */
 #define REFRESH_IGNORE_SUBMODULES      0x0010  /* ignore submodules */
 #define REFRESH_IN_PORCELAIN   0x0020  /* user friendly output, not "needs update" */
+#define REFRESH_PROGRESS       0x0040  /* show progress bar if stderr is tty */
 extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
 extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
 
@@ -1482,6 +1487,7 @@ extern const char *fmt_name(const char *name, const char *email);
 extern const char *ident_default_name(void);
 extern const char *ident_default_email(void);
 extern const char *git_editor(void);
+extern const char *git_sequence_editor(void);
 extern const char *git_pager(int stdout_is_tty);
 extern int is_terminal_dumb(void);
 extern int git_ident_config(const char *, const char *, void *);
@@ -1705,7 +1711,7 @@ void shift_tree_by(const struct object_id *, const struct object_id *, struct ob
 /* All WS_* -- when extended, adapt diff.c emit_symbol */
 #define WS_RULE_MASK           07777
 extern unsigned whitespace_rule_cfg;
-extern unsigned whitespace_rule(const char *);
+extern unsigned whitespace_rule(struct index_state *, const char *);
 extern unsigned parse_whitespace_rule(const char *);
 extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
 extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
@@ -1727,10 +1733,12 @@ extern struct startup_info *startup_info;
 
 /* merge.c */
 struct commit_list;
-int try_merge_command(const char *strategy, size_t xopts_nr,
+int try_merge_command(struct repository *r,
+               const char *strategy, size_t xopts_nr,
                const char **xopts, struct commit_list *common,
                const char *head_arg, struct commit_list *remotes);
-int checkout_fast_forward(const struct object_id *from,
+int checkout_fast_forward(struct repository *r,
+                         const struct object_id *from,
                          const struct object_id *to,
                          int overwrite_ignore);
 
index 2a5bff4a1ca3de1dfca6cecb9d2016173f4fdea5..cda170d5c27bab7c511d6fe70fc9852b21cc0180 100755 (executable)
@@ -15,6 +15,8 @@ then
        export GIT_TEST_FULL_IN_PACK_ARRAY=true
        export GIT_TEST_OE_SIZE=10
        export GIT_TEST_OE_DELTA_SIZE=5
+       export GIT_TEST_COMMIT_GRAPH=1
+       export GIT_TEST_MULTI_PACK_INDEX=1
        make --quiet test
 fi
 
index 0fed4ca529c7da81cde2b84bbe276e55ad87d0e4..10155e0ec8b18fa80ea96f90a508250bdc5bb9f0 100644 (file)
@@ -285,7 +285,8 @@ static struct lline *coalesce_lines(struct lline *base, int *lenbase,
        return base;
 }
 
-static char *grab_blob(const struct object_id *oid, unsigned int mode,
+static char *grab_blob(struct repository *r,
+                      const struct object_id *oid, unsigned int mode,
                       unsigned long *size, struct userdiff_driver *textconv,
                       const char *path)
 {
@@ -304,7 +305,7 @@ static char *grab_blob(const struct object_id *oid, unsigned int mode,
        } else if (textconv) {
                struct diff_filespec *df = alloc_filespec(path);
                fill_filespec(df, oid, 1, mode);
-               *size = fill_textconv(textconv, df, &blob);
+               *size = fill_textconv(r, textconv, df, &blob);
                free_filespec(df);
        } else {
                blob = read_object_file(oid, &type, size);
@@ -389,7 +390,8 @@ static void consume_line(void *state_, char *line, unsigned long len)
        }
 }
 
-static void combine_diff(const struct object_id *parent, unsigned int mode,
+static void combine_diff(struct repository *r,
+                        const struct object_id *parent, unsigned int mode,
                         mmfile_t *result_file,
                         struct sline *sline, unsigned int cnt, int n,
                         int num_parent, int result_deleted,
@@ -407,7 +409,7 @@ static void combine_diff(const struct object_id *parent, unsigned int mode,
        if (result_deleted)
                return; /* result deleted */
 
-       parent_file.ptr = grab_blob(parent, mode, &sz, textconv, path);
+       parent_file.ptr = grab_blob(r, parent, mode, &sz, textconv, path);
        parent_file.size = sz;
        memset(&xpp, 0, sizeof(xpp));
        xpp.flags = flags;
@@ -985,7 +987,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
        const char *line_prefix = diff_line_prefix(opt);
 
        context = opt->context;
-       userdiff = userdiff_find_by_path(elem->path);
+       userdiff = userdiff_find_by_path(opt->repo->index, elem->path);
        if (!userdiff)
                userdiff = userdiff_find_by_name("default");
        if (opt->flags.allow_textconv)
@@ -993,7 +995,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
 
        /* Read the result of merge first */
        if (!working_tree_file)
-               result = grab_blob(&elem->oid, elem->mode, &result_size,
+               result = grab_blob(opt->repo, &elem->oid, elem->mode, &result_size,
                                   textconv, elem->path);
        else {
                /* Used by diff-tree to read from the working tree */
@@ -1016,15 +1018,16 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                } else if (S_ISDIR(st.st_mode)) {
                        struct object_id oid;
                        if (resolve_gitlink_ref(elem->path, "HEAD", &oid) < 0)
-                               result = grab_blob(&elem->oid, elem->mode,
-                                                  &result_size, NULL, NULL);
+                               result = grab_blob(opt->repo, &elem->oid,
+                                                  elem->mode, &result_size,
+                                                  NULL, NULL);
                        else
-                               result = grab_blob(&oid, elem->mode,
+                               result = grab_blob(opt->repo, &oid, elem->mode,
                                                   &result_size, NULL, NULL);
                } else if (textconv) {
                        struct diff_filespec *df = alloc_filespec(elem->path);
                        fill_filespec(df, &null_oid, 0, st.st_mode);
-                       result_size = fill_textconv(textconv, df, &result);
+                       result_size = fill_textconv(opt->repo, textconv, df, &result);
                        free_filespec(df);
                } else if (0 <= (fd = open(elem->path, O_RDONLY))) {
                        size_t len = xsize_t(st.st_size);
@@ -1054,7 +1057,8 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        if (is_file) {
                                struct strbuf buf = STRBUF_INIT;
 
-                               if (convert_to_git(&the_index, elem->path, result, len, &buf, global_conv_flags_eol)) {
+                               if (convert_to_git(rev->diffopt.repo->index,
+                                                  elem->path, result, len, &buf, global_conv_flags_eol)) {
                                        free(result);
                                        result = strbuf_detach(&buf, &len);
                                        result_size = len;
@@ -1089,7 +1093,8 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                for (i = 0; !is_binary && i < num_parent; i++) {
                        char *buf;
                        unsigned long size;
-                       buf = grab_blob(&elem->parent[i].oid,
+                       buf = grab_blob(opt->repo,
+                                       &elem->parent[i].oid,
                                        elem->parent[i].mode,
                                        &size, NULL, NULL);
                        if (buffer_is_binary(buf, size))
@@ -1145,7 +1150,8 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        }
                }
                if (i <= j)
-                       combine_diff(&elem->parent[i].oid,
+                       combine_diff(opt->repo,
+                                    &elem->parent[i].oid,
                                     elem->parent[i].mode,
                                     &result_file, sline,
                                     cnt, i, num_parent, result_deleted,
index c36ea3c18226cb6212eb8dcbf5b6e5df5886c922..3a9af104b55c9e2de4bc82390f4d1c48e6c407c3 100644 (file)
@@ -62,7 +62,7 @@ git-check-mailmap                       purehelpers
 git-checkout                            mainporcelain           history
 git-checkout-index                      plumbingmanipulators
 git-check-ref-format                    purehelpers
-git-cherry                              ancillaryinterrogators          complete
+git-cherry                              plumbinginterrogators          complete
 git-cherry-pick                         mainporcelain
 git-citool                              mainporcelain
 git-clean                               mainporcelain
@@ -96,7 +96,7 @@ git-for-each-ref                        plumbinginterrogators
 git-format-patch                        mainporcelain
 git-fsck                                ancillaryinterrogators          complete
 git-gc                                  mainporcelain
-git-get-tar-commit-id                   ancillaryinterrogators
+git-get-tar-commit-id                   plumbinginterrogators
 git-grep                                mainporcelain           info
 git-gui                                 mainporcelain
 git-hash-object                         plumbingmanipulators
@@ -153,7 +153,7 @@ git-rerere                              ancillaryinterrogators
 git-reset                               mainporcelain           worktree
 git-revert                              mainporcelain
 git-rev-list                            plumbinginterrogators
-git-rev-parse                           ancillaryinterrogators
+git-rev-parse                           plumbinginterrogators
 git-rm                                  mainporcelain           worktree
 git-send-email                          foreignscminterface             complete
 git-send-pack                           synchingrepositories
index a6867586039b507e7802a469a1a3bed82e44bddb..40c855f1855595fbf5b285008d3c829e6a2f41eb 100644 (file)
@@ -739,11 +739,12 @@ static int add_ref_to_list(const char *refname,
 void write_commit_graph_reachable(const char *obj_dir, int append,
                                  int report_progress)
 {
-       struct string_list list;
+       struct string_list list = STRING_LIST_INIT_DUP;
 
-       string_list_init(&list, 1);
        for_each_ref(add_ref_to_list, &list);
        write_commit_graph(obj_dir, NULL, &list, append, report_progress);
+
+       string_list_clear(&list, 0);
 }
 
 void write_commit_graph(const char *obj_dir,
@@ -768,7 +769,7 @@ void write_commit_graph(const char *obj_dir,
                return;
 
        oids.nr = 0;
-       oids.alloc = approximate_object_count() / 4;
+       oids.alloc = approximate_object_count() / 32;
        oids.progress = NULL;
        oids.progress_done = 0;
 
@@ -813,6 +814,7 @@ void write_commit_graph(const char *obj_dir,
                                die(_("error opening index for %s"), packname.buf);
                        for_each_object_in_pack(p, add_packed_commits, &oids, 0);
                        close_pack(p);
+                       free(p);
                }
                stop_progress(&oids.progress);
                strbuf_release(&packname);
@@ -895,9 +897,11 @@ void write_commit_graph(const char *obj_dir,
        compute_generation_numbers(&commits, report_progress);
 
        graph_name = get_commit_graph_filename(obj_dir);
-       if (safe_create_leading_directories(graph_name))
+       if (safe_create_leading_directories(graph_name)) {
+               UNLEAK(graph_name);
                die_errno(_("unable to create leading directories of %s"),
                          graph_name);
+       }
 
        hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
        f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
@@ -942,9 +946,9 @@ void write_commit_graph(const char *obj_dir,
        finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
        commit_lock_file(&lk);
 
+       free(graph_name);
+       free(commits.list);
        free(oids.list);
-       oids.alloc = 0;
-       oids.nr = 0;
 }
 
 #define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
index 9f79ce0a22946174376d247127027e4a0ad3c1e9..a9da65c4625cbbeb165b0c2d54fe3edabb1a3a34 100644 (file)
@@ -529,8 +529,8 @@ int commit_contains(struct ref_filter *filter, struct commit *commit,
 
 static int compare_commits_by_gen(const void *_a, const void *_b)
 {
-       const struct commit *a = (const struct commit *)_a;
-       const struct commit *b = (const struct commit *)_b;
+       const struct commit *a = *(const struct commit * const *)_a;
+       const struct commit *b = *(const struct commit * const *)_b;
 
        if (a->generation < b->generation)
                return -1;
@@ -593,8 +593,10 @@ int can_all_from_reach_with_flag(struct object_array *from,
                while (stack) {
                        struct commit_list *parent;
 
-                       if (stack->item->object.flags & with_flag) {
+                       if (stack->item->object.flags & (with_flag | RESULT)) {
                                pop_commit(&stack);
+                               if (stack)
+                                       stack->item->object.flags |= RESULT;
                                continue;
                        }
 
index 7d313e2975a28ef79b2ccf6dea965d4f139eda58..122a23a24d8a40d213c7a1fb167f3f9c2b3c73d6 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __COMMIT_REACH_H__
-#define __COMMIT_REACH_H__
+#ifndef COMMIT_REACH_H
+#define COMMIT_REACH_H
 
 #include "commit-slab.h"
 
index ac1e6d409ad6cf1109f1e439944bd2fb1ab9e58e..5c0eb91a5d175ddb079459a0ba13b9be1f74b1ab 100644 (file)
@@ -1,10 +1,10 @@
 #ifndef COMMIT_SLAB_IMPL_H
 #define COMMIT_SLAB_IMPL_H
 
-#define MAYBE_UNUSED __attribute__((__unused__))
+#include "git-compat-util.h"
 
 #define implement_static_commit_slab(slabname, elemtype) \
-       implement_commit_slab(slabname, elemtype, static MAYBE_UNUSED)
+       implement_commit_slab(slabname, elemtype, MAYBE_UNUSED static)
 
 #define implement_shared_commit_slab(slabname, elemtype) \
        implement_commit_slab(slabname, elemtype, )
index dc8a39d52a1c31f979068a34bbdeeca14f3a7547..d566d7e45c17cfa53493f228dbe46f2f92713ac9 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -17,6 +17,8 @@
 #include "sha1-lookup.h"
 #include "wt-status.h"
 #include "advice.h"
+#include "refs.h"
+#include "commit-reach.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
@@ -843,6 +845,86 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so
                clear_author_date_slab(&author_date);
 }
 
+struct rev_collect {
+       struct commit **commit;
+       int nr;
+       int alloc;
+       unsigned int initial : 1;
+};
+
+static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
+{
+       struct commit *commit;
+
+       if (is_null_oid(oid))
+               return;
+
+       commit = lookup_commit(the_repository, oid);
+       if (!commit ||
+           (commit->object.flags & TMP_MARK) ||
+           parse_commit(commit))
+               return;
+
+       ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
+       revs->commit[revs->nr++] = commit;
+       commit->object.flags |= TMP_MARK;
+}
+
+static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
+                                 const char *ident, timestamp_t timestamp,
+                                 int tz, const char *message, void *cbdata)
+{
+       struct rev_collect *revs = cbdata;
+
+       if (revs->initial) {
+               revs->initial = 0;
+               add_one_commit(ooid, revs);
+       }
+       add_one_commit(noid, revs);
+       return 0;
+}
+
+struct commit *get_fork_point(const char *refname, struct commit *commit)
+{
+       struct object_id oid;
+       struct rev_collect revs;
+       struct commit_list *bases;
+       int i;
+       struct commit *ret = NULL;
+
+       memset(&revs, 0, sizeof(revs));
+       revs.initial = 1;
+       for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
+
+       if (!revs.nr && !get_oid(refname, &oid))
+               add_one_commit(&oid, &revs);
+
+       for (i = 0; i < revs.nr; i++)
+               revs.commit[i]->object.flags &= ~TMP_MARK;
+
+       bases = get_merge_bases_many(commit, revs.nr, revs.commit);
+
+       /*
+        * There should be one and only one merge base, when we found
+        * a common ancestor among reflog entries.
+        */
+       if (!bases || bases->next)
+               goto cleanup_return;
+
+       /* And the found one must be one of the reflog entries */
+       for (i = 0; i < revs.nr; i++)
+               if (&bases->item->object == &revs.commit[i]->object)
+                       break; /* found */
+       if (revs.nr <= i)
+               goto cleanup_return;
+
+       ret = bases->item;
+
+cleanup_return:
+       free_commit_list(bases);
+       return ret;
+}
+
 static const char gpg_sig_header[] = "gpgsig";
 static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
 
index 1d260d62f57a24864986252892faa89c17572210..8f15cfd43b602f0b85eda82fb9af90eef7996b6c 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -205,6 +205,8 @@ int register_commit_graft(struct repository *r, struct commit_graft *, int);
 void prepare_commit_graft(struct repository *r);
 struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid);
 
+struct commit *get_fork_point(const char *refname, struct commit *commit);
+
 /* largest positive number a signed 32-bit integer can contain */
 #define INFINITE_DEPTH 0x7fffffff
 
@@ -249,7 +251,9 @@ extern void assign_shallow_commits_to_refs(struct shallow_info *info,
                                           uint32_t **used,
                                           int *ref_status);
 extern int delayed_reachability_test(struct shallow_info *si, int c);
-extern void prune_shallow(int show_only);
+#define PRUNE_SHOW_ONLY 1
+#define PRUNE_QUICK 2
+extern void prune_shallow(unsigned options);
 extern struct trace_key trace_shallow;
 
 extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
index 18caf21969a5917e8e6c6d754beffbba2741e6c2..81ef24286a2757920e411b3858bcbdfd51d69a3b 100644 (file)
@@ -5,6 +5,7 @@
 #include "../strbuf.h"
 #include "../run-command.h"
 #include "../cache.h"
+#include "win32/lazyload.h"
 
 #define HCAST(type, handle) ((type)(intptr_t)handle)
 
@@ -202,6 +203,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
        }
 }
 
+/* Normalizes NT paths as returned by some low-level APIs. */
+static wchar_t *normalize_ntpath(wchar_t *wbuf)
+{
+       int i;
+       /* fix absolute path prefixes */
+       if (wbuf[0] == '\\') {
+               /* strip NT namespace prefixes */
+               if (!wcsncmp(wbuf, L"\\??\\", 4) ||
+                   !wcsncmp(wbuf, L"\\\\?\\", 4))
+                       wbuf += 4;
+               else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
+                       wbuf += 12;
+               /* replace remaining '...UNC\' with '\\' */
+               if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
+                       wbuf += 2;
+                       *wbuf = '\\';
+               }
+       }
+       /* convert backslashes to slashes */
+       for (i = 0; wbuf[i]; i++)
+               if (wbuf[i] == '\\')
+                       wbuf[i] = '/';
+       return wbuf;
+}
+
 int mingw_unlink(const char *pathname)
 {
        int ret, tries = 0;
@@ -917,8 +943,29 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
 
 char *mingw_getcwd(char *pointer, int len)
 {
-       wchar_t wpointer[MAX_PATH];
-       if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
+       wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
+       DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
+
+       if (!ret || ret >= ARRAY_SIZE(cwd)) {
+               errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());
+               return NULL;
+       }
+       ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
+       if (!ret && GetLastError() == ERROR_ACCESS_DENIED) {
+               HANDLE hnd = CreateFileW(cwd, 0,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                       OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+               if (hnd == INVALID_HANDLE_VALUE)
+                       return NULL;
+               ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
+               CloseHandle(hnd);
+               if (!ret || ret >= ARRAY_SIZE(wpointer))
+                       return NULL;
+               if (xwcstoutf(pointer, normalize_ntpath(wpointer), len) < 0)
+                       return NULL;
+               return pointer;
+       }
+       if (!ret || ret >= ARRAY_SIZE(wpointer))
                return NULL;
        if (xwcstoutf(pointer, wpointer, len) < 0)
                return NULL;
@@ -1577,7 +1624,8 @@ static void ensure_socket_initialization(void)
                        WSAGetLastError());
 
        for (name = libraries; *name; name++) {
-               ipv6_dll = LoadLibrary(*name);
+               ipv6_dll = LoadLibraryExA(*name, NULL,
+                                         LOAD_LIBRARY_SEARCH_SYSTEM32);
                if (!ipv6_dll)
                        continue;
 
@@ -1798,18 +1846,63 @@ int mingw_getpagesize(void)
        return si.dwAllocationGranularity;
 }
 
+/* See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724435.aspx */
+enum EXTENDED_NAME_FORMAT {
+       NameDisplay = 3,
+       NameUserPrincipal = 8
+};
+
+static char *get_extended_user_info(enum EXTENDED_NAME_FORMAT type)
+{
+       DECLARE_PROC_ADDR(secur32.dll, BOOL, GetUserNameExW,
+               enum EXTENDED_NAME_FORMAT, LPCWSTR, PULONG);
+       static wchar_t wbuffer[1024];
+       DWORD len;
+
+       if (!INIT_PROC_ADDR(GetUserNameExW))
+               return NULL;
+
+       len = ARRAY_SIZE(wbuffer);
+       if (GetUserNameExW(type, wbuffer, &len)) {
+               char *converted = xmalloc((len *= 3));
+               if (xwcstoutf(converted, wbuffer, len) >= 0)
+                       return converted;
+               free(converted);
+       }
+
+       return NULL;
+}
+
+char *mingw_query_user_email(void)
+{
+       return get_extended_user_info(NameUserPrincipal);
+}
+
 struct passwd *getpwuid(int uid)
 {
+       static unsigned initialized;
        static char user_name[100];
-       static struct passwd p;
+       static struct passwd *p;
+       DWORD len;
+
+       if (initialized)
+               return p;
 
-       DWORD len = sizeof(user_name);
-       if (!GetUserName(user_name, &len))
+       len = sizeof(user_name);
+       if (!GetUserName(user_name, &len)) {
+               initialized = 1;
                return NULL;
-       p.pw_name = user_name;
-       p.pw_gecos = "unknown";
-       p.pw_dir = NULL;
-       return &p;
+       }
+
+       p = xmalloc(sizeof(*p));
+       p->pw_name = user_name;
+       p->pw_gecos = get_extended_user_info(NameDisplay);
+       if (!p->pw_gecos)
+               p->pw_gecos = "unknown";
+       p->pw_dir = NULL;
+
+       initialized = 1;
+       return p;
 }
 
 static HANDLE timer_event;
index 571019d0bddceaf3245e15dbcc4ebfb70a501d17..f31dcff2be1d60ce4f5ae46089d59255f1bc4dff 100644 (file)
@@ -424,6 +424,8 @@ static inline void convert_slashes(char *path)
 int mingw_offset_1st_component(const char *path);
 #define offset_1st_component mingw_offset_1st_component
 #define PATH_SEP ';'
+extern char *mingw_query_user_email(void);
+#define query_user_email mingw_query_user_email
 #if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
 #define PRIuMAX "I64u"
 #define PRId64 "I64d"
index 7f662fef7bcb408045eb1536afed058a607ae97b..14d31010dfe57e520c6864df4f84ea732018785f 100644 (file)
@@ -4,7 +4,7 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of
 {
        size_t n = 0;
 
-       if (start != NULL || !(flags & MAP_PRIVATE))
+       if (start != NULL || flags != MAP_PRIVATE || prot != PROT_READ)
                die("Invalid usage of mmap when built with NO_MMAP");
 
        start = xmalloc(length);
index 3461993f0af665d64b48d16915fc2cd017634377..4051e388230afb1fe96f1ffad35fcc36425d247b 100644 (file)
--- a/config.c
+++ b/config.c
@@ -2278,7 +2278,7 @@ int git_config_get_max_percent_split_change(void)
 int git_config_get_fsmonitor(void)
 {
        if (git_config_get_pathname("core.fsmonitor", &core_fsmonitor))
-               core_fsmonitor = getenv("GIT_FSMONITOR_TEST");
+               core_fsmonitor = getenv("GIT_TEST_FSMONITOR");
 
        if (core_fsmonitor && !*core_fsmonitor)
                core_fsmonitor = NULL;
@@ -2289,6 +2289,24 @@ int git_config_get_fsmonitor(void)
        return 0;
 }
 
+int git_config_get_index_threads(void)
+{
+       int is_bool, val = 0;
+
+       val = git_env_ulong("GIT_TEST_INDEX_THREADS", 0);
+       if (val)
+               return val;
+
+       if (!git_config_get_bool_or_int("index.threads", &is_bool, &val)) {
+               if (is_bool)
+                       return val ? 0 : 1;
+               else
+                       return val;
+       }
+
+       return 0; /* auto */
+}
+
 NORETURN
 void git_die_config_linenr(const char *key, const char *filename, int linenr)
 {
index ab46e0165d8ffa782a796d0e2b23337aa9004b51..a06027e69b9d453c6e4277968622aa9bd6ae92b1 100644 (file)
--- a/config.h
+++ b/config.h
@@ -250,6 +250,7 @@ extern int git_config_get_untracked_cache(void);
 extern int git_config_get_split_index(void);
 extern int git_config_get_max_percent_split_change(void);
 extern int git_config_get_fsmonitor(void);
+extern int git_config_get_index_threads(void);
 
 /* This dies if the configured or default date is in the future */
 extern int git_config_get_expiry(const char *key, const char **output);
index 92d268137f61e825c1529e5eedca9e39c539dce2..bbeeff44fe1e9b4a864cd0dc082906041d65de51 100644 (file)
@@ -34,7 +34,6 @@ ifeq ($(filter extra-all,$(DEVOPTS)),)
 CFLAGS += -Wno-empty-body
 CFLAGS += -Wno-missing-field-initializers
 CFLAGS += -Wno-sign-compare
-CFLAGS += -Wno-unused-function
 CFLAGS += -Wno-unused-parameter
 endif
 endif
index e4c961817d33f64286d3415b370e6a297e381559..8d5a6b3ad6fe4bb0f9ca0930f8eea82543968306 100644 (file)
@@ -51,9 +51,9 @@ struct check_connected_options {
 #define CHECK_CONNECTED_INIT { 0 }
 
 /*
- * Make sure that our object store has all the commits necessary to
- * connect the ancestry chain to some of our existing refs, and all
- * the trees and blobs that these commits use.
+ * Make sure that all given objects and all objects reachable from them
+ * either exist in our object store or (if the repository is a partial
+ * clone) are promised to be available.
  *
  * Return 0 if Ok, non zero otherwise (i.e. some missing objects)
  *
index d8bdb4871273da92d937d30a787d8b149fbe1552..6a7cf3e02da832b3a2829178b968b57f3b80d081 100644 (file)
 @@
-expression E1;
+struct object_id OID;
 @@
-- is_null_sha1(E1.hash)
-+ is_null_oid(&E1)
+- is_null_sha1(OID.hash)
++ is_null_oid(&OID)
 
 @@
-expression E1;
+struct object_id *OIDPTR;
 @@
-- is_null_sha1(E1->hash)
-+ is_null_oid(E1)
+- is_null_sha1(OIDPTR->hash)
++ is_null_oid(OIDPTR)
 
 @@
-expression E1;
+struct object_id OID;
 @@
-- sha1_to_hex(E1.hash)
-+ oid_to_hex(&E1)
+- sha1_to_hex(OID.hash)
++ oid_to_hex(&OID)
 
 @@
 identifier f != oid_to_hex;
-expression E1;
+struct object_id *OIDPTR;
 @@
   f(...) {<...
-- sha1_to_hex(E1->hash)
-+ oid_to_hex(E1)
+- sha1_to_hex(OIDPTR->hash)
++ oid_to_hex(OIDPTR)
   ...>}
 
 @@
-expression E1, E2;
+expression E;
+struct object_id OID;
 @@
-- sha1_to_hex_r(E1, E2.hash)
-+ oid_to_hex_r(E1, &E2)
+- sha1_to_hex_r(E, OID.hash)
++ oid_to_hex_r(E, &OID)
 
 @@
 identifier f != oid_to_hex_r;
-expression E1, E2;
+expression E;
+struct object_id *OIDPTR;
 @@
    f(...) {<...
-- sha1_to_hex_r(E1, E2->hash)
-+ oid_to_hex_r(E1, E2)
+- sha1_to_hex_r(E, OIDPTR->hash)
++ oid_to_hex_r(E, OIDPTR)
   ...>}
 
 @@
-expression E1;
+struct object_id OID;
 @@
-- hashclr(E1.hash)
-+ oidclr(&E1)
+- hashclr(OID.hash)
++ oidclr(&OID)
 
 @@
 identifier f != oidclr;
-expression E1;
+struct object_id *OIDPTR;
 @@
   f(...) {<...
-- hashclr(E1->hash)
-+ oidclr(E1)
+- hashclr(OIDPTR->hash)
++ oidclr(OIDPTR)
   ...>}
 
 @@
-expression E1, E2;
+struct object_id OID1, OID2;
 @@
-- hashcmp(E1.hash, E2.hash)
-+ oidcmp(&E1, &E2)
+- hashcmp(OID1.hash, OID2.hash)
++ oidcmp(&OID1, &OID2)
 
 @@
 identifier f != oidcmp;
-expression E1, E2;
+struct object_id *OIDPTR1, OIDPTR2;
 @@
   f(...) {<...
-- hashcmp(E1->hash, E2->hash)
-+ oidcmp(E1, E2)
+- hashcmp(OIDPTR1->hash, OIDPTR2->hash)
++ oidcmp(OIDPTR1, OIDPTR2)
   ...>}
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR;
+struct object_id OID;
 @@
-- hashcmp(E1->hash, E2.hash)
-+ oidcmp(E1, &E2)
+- hashcmp(OIDPTR->hash, OID.hash)
++ oidcmp(OIDPTR, &OID)
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR;
+struct object_id OID;
 @@
-- hashcmp(E1.hash, E2->hash)
-+ oidcmp(&E1, E2)
+- hashcmp(OID.hash, OIDPTR->hash)
++ oidcmp(&OID, OIDPTR)
 
 @@
-expression E1, E2;
+struct object_id OID1, OID2;
 @@
-- hashcpy(E1.hash, E2.hash)
-+ oidcpy(&E1, &E2)
+- hashcpy(OID1.hash, OID2.hash)
++ oidcpy(&OID1, &OID2)
 
 @@
 identifier f != oidcpy;
-expression E1, E2;
+struct object_id *OIDPTR1;
+struct object_id *OIDPTR2;
 @@
   f(...) {<...
-- hashcpy(E1->hash, E2->hash)
-+ oidcpy(E1, E2)
+- hashcpy(OIDPTR1->hash, OIDPTR2->hash)
++ oidcpy(OIDPTR1, OIDPTR2)
   ...>}
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR;
+struct object_id OID;
 @@
-- hashcpy(E1->hash, E2.hash)
-+ oidcpy(E1, &E2)
+- hashcpy(OIDPTR->hash, OID.hash)
++ oidcpy(OIDPTR, &OID)
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR;
+struct object_id OID;
 @@
-- hashcpy(E1.hash, E2->hash)
-+ oidcpy(&E1, E2)
+- hashcpy(OID.hash, OIDPTR->hash)
++ oidcpy(&OID, OIDPTR)
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR1;
+struct object_id *OIDPTR2;
 @@
-- oidcmp(E1, E2) == 0
-+ oideq(E1, E2)
+- oidcmp(OIDPTR1, OIDPTR2) == 0
++ oideq(OIDPTR1, OIDPTR2)
 
 @@
 identifier f != hasheq;
@@ -125,10 +133,11 @@ expression E1, E2;
   ...>}
 
 @@
-expression E1, E2;
+struct object_id *OIDPTR1;
+struct object_id *OIDPTR2;
 @@
-- oidcmp(E1, E2) != 0
-+ !oideq(E1, E2)
+- oidcmp(OIDPTR1, OIDPTR2) != 0
++ !oideq(OIDPTR1, OIDPTR2)
 
 @@
 identifier f != hasheq;
diff --git a/contrib/coccinelle/preincr.cocci b/contrib/coccinelle/preincr.cocci
new file mode 100644 (file)
index 0000000..7fe1e8d
--- /dev/null
@@ -0,0 +1,5 @@
+@ preincrement @
+identifier i;
+@@
+- ++i > 1
++ i++
index 06ec6ca11399b21c6de5c0f5678dcb94d3925008..86b8f474c8d5af0e5a948b835ba104fd963cbff5 100644 (file)
@@ -943,6 +943,7 @@ __git_complete_remote_or_refspec ()
                        *) ;;
                        esac
                        ;;
+               --multiple) no_complete_refspec=1; break ;;
                -*) ;;
                *) remote="$i"; break ;;
                esac
@@ -1340,17 +1341,6 @@ _git_checkout ()
        esac
 }
 
-_git_cherry ()
-{
-       case "$cur" in
-       --*)
-               __gitcomp_builtin cherry
-               return
-       esac
-
-       __git_complete_refs
-}
-
 __git_cherry_pick_inprogress_options="--continue --quit --abort"
 
 _git_cherry_pick ()
@@ -1832,7 +1822,7 @@ _git_mergetool ()
                return
                ;;
        --*)
-               __gitcomp "--tool= --prompt --no-prompt"
+               __gitcomp "--tool= --prompt --no-prompt --gui --no-gui"
                return
                ;;
        esac
diff --git a/contrib/coverage-diff.sh b/contrib/coverage-diff.sh
new file mode 100755 (executable)
index 0000000..4ec419f
--- /dev/null
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+# Usage: Run 'contrib/coverage-diff.sh <version1> <version2>' from source-root
+# after running
+#
+#     make coverage-test
+#     make coverage-report
+#
+# while checked out at <version2>. This script combines the *.gcov files
+# generated by the 'make' commands above with 'git diff <version1> <version2>'
+# to report new lines that are not covered by the test suite.
+
+V1=$1
+V2=$2
+
+diff_lines () {
+       perl -e '
+               my $line_num;
+               while (<>) {
+                       # Hunk header?  Grab the beginning in postimage.
+                       if (/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/) {
+                               $line_num = $1;
+                               next;
+                       }
+
+                       # Have we seen a hunk?  Ignore "diff --git" etc.
+                       next unless defined $line_num;
+
+                       # Deleted line? Ignore.
+                       if (/^-/) {
+                               next;
+                       }
+
+                       # Show only the line number of added lines.
+                       if (/^\+/) {
+                               print "$line_num\n";
+                       }
+                       # Either common context or added line appear in
+                       # the postimage.  Count it.
+                       $line_num++;
+               }
+       '
+}
+
+files=$(git diff --name-only "$V1" "$V2" -- \*.c)
+
+# create empty file
+>coverage-data.txt
+
+for file in $files
+do
+       git diff "$V1" "$V2" -- "$file" |
+       diff_lines |
+       sort >new_lines.txt
+
+       if ! test -s new_lines.txt
+       then
+               continue
+       fi
+
+       hash_file=$(echo $file | sed "s/\//\#/")
+
+       if ! test -s "$hash_file.gcov"
+       then
+               continue
+       fi
+
+       sed -ne '/#####:/{
+                       s/    #####://
+                       s/:.*//
+                       s/ //g
+                       p
+               }' "$hash_file.gcov" |
+       sort >uncovered_lines.txt
+
+       comm -12 uncovered_lines.txt new_lines.txt |
+       sed -e 's/$/\)/' |
+       sed -e 's/^/ /' >uncovered_new_lines.txt
+
+       grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
+       echo $file >>coverage-data.txt &&
+       git blame -s "$V2" -- "$file" |
+       sed 's/\t//g' |
+       grep -f uncovered_new_lines.txt >>coverage-data.txt &&
+       echo >>coverage-data.txt
+
+       rm -f new_lines.txt uncovered_lines.txt uncovered_new_lines.txt
+done
+
+cat coverage-data.txt
+
+echo "Commits introducing uncovered code:"
+
+commit_list=$(cat coverage-data.txt |
+       grep -E '^[0-9a-f]{7,} ' |
+       awk '{print $1;}' |
+       sort |
+       uniq)
+
+(
+       for commit in $commit_list
+       do
+               git log --no-decorate --pretty=format:'%an      %h: %s' -1 $commit
+               echo
+       done
+) | sort
+
+rm coverage-data.txt
index 86518cd93d9c58d8b9f02d7cb8a67adf7b3fb3b6..5bdad41de1f834b0685390258161a9e749d0dca0 100644 (file)
@@ -75,7 +75,8 @@ static CredDeleteWT CredDeleteW;
 static void load_cred_funcs(void)
 {
        /* load DLLs */
-       advapi = LoadLibrary("advapi32.dll");
+       advapi = LoadLibraryExA("advapi32.dll", NULL,
+                               LOAD_LIBRARY_SEARCH_SYSTEM32);
        if (!advapi)
                die("failed to load advapi32.dll");
 
index 5c6cc4ab2c270acda160df7a5997266d0076d504..6906aae44147810ec2cf7560eb2cae3ef56bb454 100644 (file)
@@ -59,6 +59,10 @@ $(GIT_SUBTREE): $(GIT_SUBTREE_SH)
 
 doc: $(GIT_SUBTREE_DOC) $(GIT_SUBTREE_HTML)
 
+man: $(GIT_SUBTREE_DOC)
+
+html: $(GIT_SUBTREE_HTML)
+
 install: $(GIT_SUBTREE)
        $(INSTALL) -d -m 755 $(DESTDIR)$(gitexecdir)
        $(INSTALL) -m 755 $(GIT_SUBTREE) $(DESTDIR)$(gitexecdir)
index d3f39a862ac9f6ec39bd08b1c3b48ea787bba04b..147201dc6c5786c9cfedbbbff0df0b1dced171db 100755 (executable)
@@ -231,12 +231,14 @@ cache_miss () {
 }
 
 check_parents () {
-       missed=$(cache_miss "$@")
+       missed=$(cache_miss "$1")
+       local indent=$(($2 + 1))
        for miss in $missed
        do
                if ! test -r "$cachedir/notree/$miss"
                then
                        debug "  incorrect order: $miss"
+                       process_split_commit "$miss" "" "$indent"
                fi
        done
 }
@@ -340,7 +342,12 @@ find_existing_splits () {
        revs="$2"
        main=
        sub=
-       git log --grep="^git-subtree-dir: $dir/*\$" \
+       local grep_format="^git-subtree-dir: $dir/*\$"
+       if test -n "$ignore_joins"
+       then
+               grep_format="^Add '$dir/' from commit '"
+       fi
+       git log --grep="$grep_format" \
                --no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
        while read a b junk
        do
@@ -534,6 +541,7 @@ copy_or_skip () {
        nonidentical=
        p=
        gotparents=
+       copycommit=
        for parent in $newparents
        do
                ptree=$(toptree_for_commit $parent) || exit $?
@@ -541,7 +549,24 @@ copy_or_skip () {
                if test "$ptree" = "$tree"
                then
                        # an identical parent could be used in place of this rev.
-                       identical="$parent"
+                       if test -n "$identical"
+                       then
+                               # if a previous identical parent was found, check whether
+                               # one is already an ancestor of the other
+                               mergebase=$(git merge-base $identical $parent)
+                               if test "$identical" = "$mergebase"
+                               then
+                                       # current identical commit is an ancestor of parent
+                                       identical="$parent"
+                               elif test "$parent" != "$mergebase"
+                               then
+                                       # no common history; commit must be copied
+                                       copycommit=1
+                               fi
+                       else
+                               # first identical parent detected
+                               identical="$parent"
+                       fi
                else
                        nonidentical="$parent"
                fi
@@ -564,7 +589,6 @@ copy_or_skip () {
                fi
        done
 
-       copycommit=
        if test -n "$identical" && test -n "$nonidentical"
        then
                extras=$(git rev-list --count $identical..$nonidentical)
@@ -598,6 +622,58 @@ ensure_valid_ref_format () {
                die "'$1' does not look like a ref"
 }
 
+process_split_commit () {
+       local rev="$1"
+       local parents="$2"
+       local indent=$3
+
+       if test $indent -eq 0
+       then
+               revcount=$(($revcount + 1))
+       else
+               # processing commit without normal parent information;
+               # fetch from repo
+               parents=$(git rev-parse "$rev^@")
+               extracount=$(($extracount + 1))
+       fi
+
+       progress "$revcount/$revmax ($createcount) [$extracount]"
+
+       debug "Processing commit: $rev"
+       exists=$(cache_get "$rev")
+       if test -n "$exists"
+       then
+               debug "  prior: $exists"
+               return
+       fi
+       createcount=$(($createcount + 1))
+       debug "  parents: $parents"
+       check_parents "$parents" "$indent"
+       newparents=$(cache_get $parents)
+       debug "  newparents: $newparents"
+
+       tree=$(subtree_for_commit "$rev" "$dir")
+       debug "  tree is: $tree"
+
+       # ugly.  is there no better way to tell if this is a subtree
+       # vs. a mainline commit?  Does it matter?
+       if test -z "$tree"
+       then
+               set_notree "$rev"
+               if test -n "$newparents"
+               then
+                       cache_set "$rev" "$rev"
+               fi
+               return
+       fi
+
+       newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
+       debug "  newrev is: $newrev"
+       cache_set "$rev" "$newrev"
+       cache_set latest_new "$newrev"
+       cache_set latest_old "$rev"
+}
+
 cmd_add () {
        if test -e "$dir"
        then
@@ -689,12 +765,7 @@ cmd_split () {
                done
        fi
 
-       if test -n "$ignore_joins"
-       then
-               unrevs=
-       else
-               unrevs="$(find_existing_splits "$dir" "$revs")"
-       fi
+       unrevs="$(find_existing_splits "$dir" "$revs")"
 
        # We can't restrict rev-list to only $dir here, because some of our
        # parents have the $dir contents the root, and those won't match.
@@ -703,45 +774,11 @@ cmd_split () {
        revmax=$(eval "$grl" | wc -l)
        revcount=0
        createcount=0
+       extracount=0
        eval "$grl" |
        while read rev parents
        do
-               revcount=$(($revcount + 1))
-               progress "$revcount/$revmax ($createcount)"
-               debug "Processing commit: $rev"
-               exists=$(cache_get "$rev")
-               if test -n "$exists"
-               then
-                       debug "  prior: $exists"
-                       continue
-               fi
-               createcount=$(($createcount + 1))
-               debug "  parents: $parents"
-               newparents=$(cache_get $parents)
-               debug "  newparents: $newparents"
-
-               tree=$(subtree_for_commit "$rev" "$dir")
-               debug "  tree is: $tree"
-
-               check_parents $parents
-
-               # ugly.  is there no better way to tell if this is a subtree
-               # vs. a mainline commit?  Does it matter?
-               if test -z "$tree"
-               then
-                       set_notree "$rev"
-                       if test -n "$newparents"
-                       then
-                               cache_set "$rev" "$rev"
-                       fi
-                       continue
-               fi
-
-               newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
-               debug "  newrev is: $newrev"
-               cache_set "$rev" "$newrev"
-               cache_set latest_new "$newrev"
-               cache_set latest_old "$rev"
+               process_split_commit "$rev" "$parents" 0
        done || exit $?
 
        latest_new=$(cache_get latest_new)
index 30bf9a2399fa392b31338489278e0363a55eb726..83fce51518854754ee6f4d95cca86d61cc10e4cf 100644 (file)
@@ -70,7 +70,7 @@ static int match_stat_with_submodule(struct diff_options *diffopt,
                                     struct stat *st, unsigned ce_option,
                                     unsigned *dirty_submodule)
 {
-       int changed = ce_match_stat(ce, st, ce_option);
+       int changed = ie_match_stat(diffopt->repo->index, ce, st, ce_option);
        if (S_ISGITLINK(ce->ce_mode)) {
                struct diff_flags orig_flags = diffopt->flags;
                if (!diffopt->flags.override_submodule_config)
@@ -93,15 +93,16 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
        unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
                              ? CE_MATCH_RACY_IS_DIRTY : 0);
        uint64_t start = getnanotime();
+       struct index_state *istate = revs->diffopt.repo->index;
 
        diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
 
        if (diff_unmerged_stage < 0)
                diff_unmerged_stage = 2;
-       entries = active_nr;
+       entries = istate->cache_nr;
        for (i = 0; i < entries; i++) {
                unsigned int oldmode, newmode;
-               struct cache_entry *ce = active_cache[i];
+               struct cache_entry *ce = istate->cache[i];
                int changed;
                unsigned dirty_submodule = 0;
                const struct object_id *old_oid, *new_oid;
@@ -109,7 +110,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                if (diff_can_quit_early(&revs->diffopt))
                        break;
 
-               if (!ce_path_match(&the_index, ce, &revs->prune_data, NULL))
+               if (!ce_path_match(istate, ce, &revs->prune_data, NULL))
                        continue;
 
                if (ce_stage(ce)) {
@@ -145,7 +146,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        dpath->mode = wt_mode;
 
                        while (i < entries) {
-                               struct cache_entry *nce = active_cache[i];
+                               struct cache_entry *nce = istate->cache[i];
                                int stage;
 
                                if (strcmp(ce->name, nce->name))
@@ -474,7 +475,9 @@ static int oneway_diff(const struct cache_entry * const *src,
        if (tree == o->df_conflict_entry)
                tree = NULL;
 
-       if (ce_path_match(&the_index, idx ? idx : tree, &revs->prune_data, NULL)) {
+       if (ce_path_match(revs->diffopt.repo->index,
+                         idx ? idx : tree,
+                         &revs->prune_data, NULL)) {
                do_oneway_diff(o, idx, tree);
                if (diff_can_quit_early(&revs->diffopt)) {
                        o->exiting_early = 1;
@@ -506,7 +509,7 @@ static int diff_cache(struct rev_info *revs,
        opts.merge = 1;
        opts.fn = oneway_diff;
        opts.unpack_data = revs;
-       opts.src_index = &the_index;
+       opts.src_index = revs->diffopt.repo->index;
        opts.dst_index = NULL;
        opts.pathspec = &revs->diffopt.pathspec;
        opts.pathspec->recursive = 1;
@@ -539,7 +542,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 {
        struct rev_info revs;
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        copy_pathspec(&revs.prune_data, &opt->pathspec);
        revs.diffopt = *opt;
 
@@ -554,7 +557,7 @@ int index_differs_from(const char *def, const struct diff_flags *flags,
        struct rev_info rev;
        struct setup_revision_opt opt;
 
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        memset(&opt, 0, sizeof(opt));
        opt.def = def;
        setup_revisions(0, NULL, &rev, &opt);
index 0ed5f0f496882cef9e04f9a93fe718a9f55a268a..9414e922d164e96ffedfa2504045930b058dbc80 100644 (file)
@@ -233,7 +233,8 @@ static void fixup_paths(const char **path, struct strbuf *replacement)
        }
 }
 
-void diff_no_index(struct rev_info *revs,
+void diff_no_index(struct repository *r,
+                  struct rev_info *revs,
                   int argc, const char **argv)
 {
        int i;
@@ -241,7 +242,11 @@ void diff_no_index(struct rev_info *revs,
        struct strbuf replacement = STRBUF_INIT;
        const char *prefix = revs->prefix;
 
-       diff_setup(&revs->diffopt);
+       /*
+        * FIXME: --no-index should not look at index and we should be
+        * able to pass NULL repo. Maybe later.
+        */
+       repo_diff_setup(r, &revs->diffopt);
        for (i = 1; i < argc - 2; ) {
                int j;
                if (!strcmp(argv[i], "--no-index"))
diff --git a/diff.c b/diff.c
index f0c7557b40443da060c3070c602dd03f49b0d689..8647db3d307c297448c91b328b1dabf19635fb94 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -554,14 +554,15 @@ static int count_lines(const char *data, int size)
        return count;
 }
 
-static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
+static int fill_mmfile(struct repository *r, mmfile_t *mf,
+                      struct diff_filespec *one)
 {
        if (!DIFF_FILE_VALID(one)) {
                mf->ptr = (char *)""; /* does not matter */
                mf->size = 0;
                return 0;
        }
-       else if (diff_populate_filespec(one, 0))
+       else if (diff_populate_filespec(r, one, 0))
                return -1;
 
        mf->ptr = one->data;
@@ -570,11 +571,12 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
 }
 
 /* like fill_mmfile, but only for size, so we can avoid retrieving blob */
-static unsigned long diff_filespec_size(struct diff_filespec *one)
+static unsigned long diff_filespec_size(struct repository *r,
+                                       struct diff_filespec *one)
 {
        if (!DIFF_FILE_VALID(one))
                return 0;
-       diff_populate_filespec(one, CHECK_SIZE_ONLY);
+       diff_populate_filespec(r, one, CHECK_SIZE_ONLY);
        return one->size;
 }
 
@@ -776,7 +778,6 @@ struct moved_entry {
        struct hashmap_entry ent;
        const struct emitted_diff_symbol *es;
        struct moved_entry *next_line;
-       struct ws_delta *wsd;
 };
 
 /**
@@ -793,6 +794,17 @@ struct ws_delta {
 };
 #define WS_DELTA_INIT { NULL, 0 }
 
+struct moved_block {
+       struct moved_entry *match;
+       struct ws_delta wsd;
+};
+
+static void moved_block_clear(struct moved_block *b)
+{
+       FREE_AND_NULL(b->wsd.string);
+       b->match = NULL;
+}
+
 static int compute_ws_delta(const struct emitted_diff_symbol *a,
                             const struct emitted_diff_symbol *b,
                             struct ws_delta *out)
@@ -801,16 +813,19 @@ static int compute_ws_delta(const struct emitted_diff_symbol *a,
        const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a;
        int d = longer->len - shorter->len;
 
+       if (strncmp(longer->line + d, shorter->line, shorter->len))
+               return 0;
+
        out->string = xmemdupz(longer->line, d);
        out->current_longer = (a == longer);
 
-       return !strncmp(longer->line + d, shorter->line, shorter->len);
+       return 1;
 }
 
 static int cmp_in_block_with_wsd(const struct diff_options *o,
                                 const struct moved_entry *cur,
                                 const struct moved_entry *match,
-                                struct moved_entry *pmb,
+                                struct moved_block *pmb,
                                 int n)
 {
        struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
@@ -830,16 +845,15 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
        if (strcmp(a, b))
                return 1;
 
-       if (!pmb->wsd)
+       if (!pmb->wsd.string)
                /*
-                * No white space delta was carried forward? This can happen
-                * when we exit early in this function and do not carry
-                * forward ws.
+                * The white space delta is not active? This can happen
+                * when we exit early in this function.
                 */
                return 1;
 
        /*
-        * The indent changes of the block are known and carried forward in
+        * 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.
         *
@@ -847,8 +861,8 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
         * one of them for the white spaces, depending which was longer.
         */
 
-       wslen = strlen(pmb->wsd->string);
-       if (pmb->wsd->current_longer) {
+       wslen = strlen(pmb->wsd.string);
+       if (pmb->wsd.current_longer) {
                c += wslen;
                cl -= wslen;
        } else {
@@ -856,7 +870,7 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
                al -= wslen;
        }
 
-       if (strcmp(a, c))
+       if (al != cl || memcmp(a, c, al))
                return 1;
 
        return 0;
@@ -898,7 +912,6 @@ static struct moved_entry *prepare_entry(struct diff_options *o,
        ret->ent.hash = xdiff_hash_string(l->line, l->len, flags);
        ret->es = l;
        ret->next_line = NULL;
-       ret->wsd = NULL;
 
        return ret;
 }
@@ -938,18 +951,18 @@ static void add_lines_to_move_detection(struct diff_options *o,
 static void pmb_advance_or_null(struct diff_options *o,
                                struct moved_entry *match,
                                struct hashmap *hm,
-                               struct moved_entry **pmb,
+                               struct moved_block *pmb,
                                int pmb_nr)
 {
        int i;
        for (i = 0; i < pmb_nr; i++) {
-               struct moved_entry *prev = pmb[i];
+               struct moved_entry *prev = pmb[i].match;
                struct moved_entry *cur = (prev && prev->next_line) ?
                                prev->next_line : NULL;
                if (cur && !hm->cmpfn(o, cur, match, NULL)) {
-                       pmb[i] = cur;
+                       pmb[i].match = cur;
                } else {
-                       pmb[i] = NULL;
+                       pmb[i].match = NULL;
                }
        }
 }
@@ -957,7 +970,7 @@ static void pmb_advance_or_null(struct diff_options *o,
 static void pmb_advance_or_null_multi_match(struct diff_options *o,
                                            struct moved_entry *match,
                                            struct hashmap *hm,
-                                           struct moved_entry **pmb,
+                                           struct moved_block *pmb,
                                            int pmb_nr, int n)
 {
        int i;
@@ -965,49 +978,47 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o,
 
        for (; match; match = hashmap_get_next(hm, match)) {
                for (i = 0; i < pmb_nr; i++) {
-                       struct moved_entry *prev = pmb[i];
+                       struct moved_entry *prev = pmb[i].match;
                        struct moved_entry *cur = (prev && prev->next_line) ?
                                        prev->next_line : NULL;
                        if (!cur)
                                continue;
-                       if (!cmp_in_block_with_wsd(o, cur, match, pmb[i], n))
+                       if (!cmp_in_block_with_wsd(o, cur, match, &pmb[i], n))
                                got_match[i] |= 1;
                }
        }
 
        for (i = 0; i < pmb_nr; i++) {
                if (got_match[i]) {
-                       /* Carry the white space delta forward */
-                       pmb[i]->next_line->wsd = pmb[i]->wsd;
-                       pmb[i] = pmb[i]->next_line;
+                       /* Advance to the next line */
+                       pmb[i].match = pmb[i].match->next_line;
                } else {
-                       if (pmb[i]->wsd) {
-                               free(pmb[i]->wsd->string);
-                               FREE_AND_NULL(pmb[i]->wsd);
-                       }
-                       pmb[i] = NULL;
+                       moved_block_clear(&pmb[i]);
                }
        }
+
+       free(got_match);
 }
 
-static int shrink_potential_moved_blocks(struct moved_entry **pmb,
+static int shrink_potential_moved_blocks(struct moved_block *pmb,
                                         int pmb_nr)
 {
        int lp, rp;
 
        /* Shrink the set of potential block to the remaining running */
        for (lp = 0, rp = pmb_nr - 1; lp <= rp;) {
-               while (lp < pmb_nr && pmb[lp])
+               while (lp < pmb_nr && pmb[lp].match)
                        lp++;
                /* lp points at the first NULL now */
 
-               while (rp > -1 && !pmb[rp])
+               while (rp > -1 && !pmb[rp].match)
                        rp--;
                /* rp points at the last non-NULL */
 
                if (lp < pmb_nr && rp > -1 && lp < rp) {
                        pmb[lp] = pmb[rp];
-                       pmb[rp] = NULL;
+                       pmb[rp].match = NULL;
+                       pmb[rp].wsd.string = NULL;
                        rp--;
                        lp++;
                }
@@ -1054,7 +1065,7 @@ static void mark_color_as_moved(struct diff_options *o,
                                struct hashmap *add_lines,
                                struct hashmap *del_lines)
 {
-       struct moved_entry **pmb = NULL; /* potentially moved blocks */
+       struct moved_block *pmb = NULL; /* potentially moved blocks */
        int pmb_nr = 0, pmb_alloc = 0;
        int n, flipped_block = 1, block_length = 0;
 
@@ -1083,7 +1094,11 @@ static void mark_color_as_moved(struct diff_options *o,
                }
 
                if (!match) {
+                       int i;
+
                        adjust_last_block(o, n, block_length);
+                       for(i = 0; i < pmb_nr; i++)
+                               moved_block_clear(&pmb[i]);
                        pmb_nr = 0;
                        block_length = 0;
                        continue;
@@ -1111,14 +1126,12 @@ static void mark_color_as_moved(struct diff_options *o,
                                ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
                                if (o->color_moved_ws_handling &
                                    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
-                                       struct ws_delta *wsd = xmalloc(sizeof(*match->wsd));
-                                       if (compute_ws_delta(l, match->es, wsd)) {
-                                               match->wsd = wsd;
-                                               pmb[pmb_nr++] = match;
-                                       } else
-                                               free(wsd);
+                                       if (compute_ws_delta(l, match->es,
+                                                            &pmb[pmb_nr].wsd))
+                                               pmb[pmb_nr++].match = match;
                                } else {
-                                       pmb[pmb_nr++] = match;
+                                       pmb[pmb_nr].wsd.string = NULL;
+                                       pmb[pmb_nr++].match = match;
                                }
                        }
 
@@ -1135,6 +1148,8 @@ static void mark_color_as_moved(struct diff_options *o,
        }
        adjust_last_block(o, n, block_length);
 
+       for(n = 0; n < pmb_nr; n++)
+               moved_block_clear(&pmb[n]);
        free(pmb);
 }
 
@@ -1202,10 +1217,11 @@ static void dim_moved_lines(struct diff_options *o)
 static void emit_line_ws_markup(struct diff_options *o,
                                const char *set_sign, const char *set,
                                const char *reset,
-                               char sign, const char *line, int len,
+                               int sign_index, const char *line, int len,
                                unsigned ws_rule, int blank_at_eof)
 {
        const char *ws = NULL;
+       int sign = o->output_indicators[sign_index];
 
        if (o->ws_error_highlight & ws_rule) {
                ws = diff_get_color_opt(o, DIFF_WHITESPACE);
@@ -1285,8 +1301,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
                                set = diff_get_color_opt(o, DIFF_FILE_OLD);
                }
                emit_line_ws_markup(o, set_sign, set, reset,
-                                   o->output_indicators[OUTPUT_INDICATOR_CONTEXT],
-                                   line, len,
+                                   OUTPUT_INDICATOR_CONTEXT, line, len,
                                    flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
                break;
        case DIFF_SYMBOL_PLUS:
@@ -1330,8 +1345,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
                        flags &= ~DIFF_SYMBOL_CONTENT_WS_MASK;
                }
                emit_line_ws_markup(o, set_sign, set, reset,
-                                   o->output_indicators[OUTPUT_INDICATOR_NEW],
-                                   line, len,
+                                   OUTPUT_INDICATOR_NEW, line, len,
                                    flags & DIFF_SYMBOL_CONTENT_WS_MASK,
                                    flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
                break;
@@ -1375,8 +1389,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
                                set = diff_get_color_opt(o, DIFF_CONTEXT_DIM);
                }
                emit_line_ws_markup(o, set_sign, set, reset,
-                                   o->output_indicators[OUTPUT_INDICATOR_OLD],
-                                   line, len,
+                                   OUTPUT_INDICATOR_OLD, line, len,
                                    flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
                break;
        case DIFF_SYMBOL_WORDS_PORCELAIN:
@@ -1714,12 +1727,12 @@ static void emit_rewrite_diff(const char *name_a,
        quote_two_c_style(&a_name, a_prefix, name_a, 0);
        quote_two_c_style(&b_name, b_prefix, name_b, 0);
 
-       size_one = fill_textconv(textconv_one, one, &data_one);
-       size_two = fill_textconv(textconv_two, two, &data_two);
+       size_one = fill_textconv(o->repo, textconv_one, one, &data_one);
+       size_two = fill_textconv(o->repo, textconv_two, two, &data_two);
 
        memset(&ecbdata, 0, sizeof(ecbdata));
        ecbdata.color_diff = want_color(o->use_color);
-       ecbdata.ws_rule = whitespace_rule(name_b);
+       ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b);
        ecbdata.opt = o;
        if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
                mmfile_t mf1, mf2;
@@ -2107,23 +2120,25 @@ static void diff_words_flush(struct emit_callback *ecbdata)
        }
 }
 
-static void diff_filespec_load_driver(struct diff_filespec *one)
+static void diff_filespec_load_driver(struct diff_filespec *one,
+                                     struct index_state *istate)
 {
        /* Use already-loaded driver */
        if (one->driver)
                return;
 
        if (S_ISREG(one->mode))
-               one->driver = userdiff_find_by_path(one->path);
+               one->driver = userdiff_find_by_path(istate, one->path);
 
        /* Fallback to default settings */
        if (!one->driver)
                one->driver = userdiff_find_by_name("default");
 }
 
-static const char *userdiff_word_regex(struct diff_filespec *one)
+static const char *userdiff_word_regex(struct diff_filespec *one,
+                                      struct index_state *istate)
 {
-       diff_filespec_load_driver(one);
+       diff_filespec_load_driver(one, istate);
        return one->driver->word_regex;
 }
 
@@ -2146,9 +2161,9 @@ static void init_diff_words_data(struct emit_callback *ecbdata,
                        xcalloc(1, sizeof(struct emitted_diff_symbols));
 
        if (!o->word_regex)
-               o->word_regex = userdiff_word_regex(one);
+               o->word_regex = userdiff_word_regex(one, o->repo->index);
        if (!o->word_regex)
-               o->word_regex = userdiff_word_regex(two);
+               o->word_regex = userdiff_word_regex(two, o->repo->index);
        if (!o->word_regex)
                o->word_regex = diff_word_regex_cfg;
        if (o->word_regex) {
@@ -2976,18 +2991,19 @@ static void show_dirstat(struct diff_options *options)
                }
 
                if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
-                       diff_populate_filespec(p->one, 0);
-                       diff_populate_filespec(p->two, 0);
-                       diffcore_count_changes(p->one, p->two, NULL, NULL,
+                       diff_populate_filespec(options->repo, p->one, 0);
+                       diff_populate_filespec(options->repo, p->two, 0);
+                       diffcore_count_changes(options->repo,
+                                              p->one, p->two, NULL, NULL,
                                               &copied, &added);
                        diff_free_filespec_data(p->one);
                        diff_free_filespec_data(p->two);
                } else if (DIFF_FILE_VALID(p->one)) {
-                       diff_populate_filespec(p->one, CHECK_SIZE_ONLY);
+                       diff_populate_filespec(options->repo, p->one, CHECK_SIZE_ONLY);
                        copied = added = 0;
                        diff_free_filespec_data(p->one);
                } else if (DIFF_FILE_VALID(p->two)) {
-                       diff_populate_filespec(p->two, CHECK_SIZE_ONLY);
+                       diff_populate_filespec(options->repo, p->two, CHECK_SIZE_ONLY);
                        copied = 0;
                        added = p->two->size;
                        diff_free_filespec_data(p->two);
@@ -3261,15 +3277,16 @@ static void emit_binary_diff(struct diff_options *o,
        emit_binary_diff_body(o, two, one);
 }
 
-int diff_filespec_is_binary(struct diff_filespec *one)
+int diff_filespec_is_binary(struct repository *r,
+                           struct diff_filespec *one)
 {
        if (one->is_binary == -1) {
-               diff_filespec_load_driver(one);
+               diff_filespec_load_driver(one, r->index);
                if (one->driver->binary != -1)
                        one->is_binary = one->driver->binary;
                else {
                        if (!one->data && DIFF_FILE_VALID(one))
-                               diff_populate_filespec(one, CHECK_BINARY);
+                               diff_populate_filespec(r, one, CHECK_BINARY);
                        if (one->is_binary == -1 && one->data)
                                one->is_binary = buffer_is_binary(one->data,
                                                one->size);
@@ -3280,9 +3297,10 @@ int diff_filespec_is_binary(struct diff_filespec *one)
        return one->is_binary;
 }
 
-static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one)
+static const struct userdiff_funcname *
+diff_funcname_pattern(struct diff_options *o, struct diff_filespec *one)
 {
-       diff_filespec_load_driver(one);
+       diff_filespec_load_driver(one, o->repo->index);
        return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
 }
 
@@ -3294,12 +3312,13 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
                options->b_prefix = b;
 }
 
-struct userdiff_driver *get_textconv(struct diff_filespec *one)
+struct userdiff_driver *get_textconv(struct index_state *istate,
+                                    struct diff_filespec *one)
 {
        if (!DIFF_FILE_VALID(one))
                return NULL;
 
-       diff_filespec_load_driver(one);
+       diff_filespec_load_driver(one, istate);
        return userdiff_get_textconv(one->driver);
 }
 
@@ -3349,8 +3368,8 @@ static void builtin_diff(const char *name_a,
        }
 
        if (o->flags.allow_textconv) {
-               textconv_one = get_textconv(one);
-               textconv_two = get_textconv(two);
+               textconv_one = get_textconv(o->repo->index, one);
+               textconv_two = get_textconv(o->repo->index, two);
        }
 
        /* Never use a non-valid filename anywhere if at all possible */
@@ -3391,13 +3410,13 @@ static void builtin_diff(const char *name_a,
                if ((one->mode ^ two->mode) & S_IFMT)
                        goto free_ab_and_return;
                if (complete_rewrite &&
-                   (textconv_one || !diff_filespec_is_binary(one)) &&
-                   (textconv_two || !diff_filespec_is_binary(two))) {
+                   (textconv_one || !diff_filespec_is_binary(o->repo, one)) &&
+                   (textconv_two || !diff_filespec_is_binary(o->repo, two))) {
                        emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
                                         header.buf, header.len, 0);
                        strbuf_reset(&header);
                        emit_rewrite_diff(name_a, name_b, one, two,
-                                               textconv_one, textconv_two, o);
+                                         textconv_one, textconv_two, o);
                        o->found_changes = 1;
                        goto free_ab_and_return;
                }
@@ -3409,8 +3428,8 @@ static void builtin_diff(const char *name_a,
                strbuf_reset(&header);
                goto free_ab_and_return;
        } else if (!o->flags.text &&
-           ( (!textconv_one && diff_filespec_is_binary(one)) ||
-             (!textconv_two && diff_filespec_is_binary(two)) )) {
+                  ( (!textconv_one && diff_filespec_is_binary(o->repo, one)) ||
+                    (!textconv_two && diff_filespec_is_binary(o->repo, two)) )) {
                struct strbuf sb = STRBUF_INIT;
                if (!one->data && !two->data &&
                    S_ISREG(one->mode) && S_ISREG(two->mode) &&
@@ -3431,7 +3450,8 @@ static void builtin_diff(const char *name_a,
                        strbuf_release(&sb);
                        goto free_ab_and_return;
                }
-               if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+               if (fill_mmfile(o->repo, &mf1, one) < 0 ||
+                   fill_mmfile(o->repo, &mf2, two) < 0)
                        die("unable to read files to diff");
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
@@ -3468,12 +3488,12 @@ static void builtin_diff(const char *name_a,
                        strbuf_reset(&header);
                }
 
-               mf1.size = fill_textconv(textconv_one, one, &mf1.ptr);
-               mf2.size = fill_textconv(textconv_two, two, &mf2.ptr);
+               mf1.size = fill_textconv(o->repo, textconv_one, one, &mf1.ptr);
+               mf2.size = fill_textconv(o->repo, textconv_two, two, &mf2.ptr);
 
-               pe = diff_funcname_pattern(one);
+               pe = diff_funcname_pattern(o, one);
                if (!pe)
-                       pe = diff_funcname_pattern(two);
+                       pe = diff_funcname_pattern(o, two);
 
                memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
@@ -3482,7 +3502,7 @@ static void builtin_diff(const char *name_a,
                        lbl[0] = NULL;
                ecbdata.label_path = lbl;
                ecbdata.color_diff = want_color(o->use_color);
-               ecbdata.ws_rule = whitespace_rule(name_b);
+               ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b);
                if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
                ecbdata.opt = o;
@@ -3582,20 +3602,21 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
 
        same_contents = oideq(&one->oid, &two->oid);
 
-       if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
+       if (diff_filespec_is_binary(o->repo, one) ||
+           diff_filespec_is_binary(o->repo, two)) {
                data->is_binary = 1;
                if (same_contents) {
                        data->added = 0;
                        data->deleted = 0;
                } else {
-                       data->added = diff_filespec_size(two);
-                       data->deleted = diff_filespec_size(one);
+                       data->added = diff_filespec_size(o->repo, two);
+                       data->deleted = diff_filespec_size(o->repo, one);
                }
        }
 
        else if (complete_rewrite) {
-               diff_populate_filespec(one, 0);
-               diff_populate_filespec(two, 0);
+               diff_populate_filespec(o->repo, one, 0);
+               diff_populate_filespec(o->repo, two, 0);
                data->deleted = count_lines(one->data, one->size);
                data->added = count_lines(two->data, two->size);
        }
@@ -3605,7 +3626,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
                xpparam_t xpp;
                xdemitconf_t xecfg;
 
-               if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+               if (fill_mmfile(o->repo, &mf1, one) < 0 ||
+                   fill_mmfile(o->repo, &mf2, two) < 0)
                        die("unable to read files to diff");
 
                memset(&xpp, 0, sizeof(xpp));
@@ -3640,10 +3662,11 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
        data.o = o;
-       data.ws_rule = whitespace_rule(attr_path);
-       data.conflict_marker_size = ll_merge_marker_size(attr_path);
+       data.ws_rule = whitespace_rule(o->repo->index, attr_path);
+       data.conflict_marker_size = ll_merge_marker_size(o->repo->index, attr_path);
 
-       if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+       if (fill_mmfile(o->repo, &mf1, one) < 0 ||
+           fill_mmfile(o->repo, &mf2, two) < 0)
                die("unable to read files to diff");
 
        /*
@@ -3652,7 +3675,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
         * introduced changes, and as long as the "new" side is text, we
         * can and should check what it introduces.
         */
-       if (diff_filespec_is_binary(two))
+       if (diff_filespec_is_binary(o->repo, two))
                goto free_and_return;
        else {
                /* Crazy xdl interfaces.. */
@@ -3725,7 +3748,10 @@ void fill_filespec(struct diff_filespec *spec, const struct object_id *oid,
  * the work tree has that object contents, return true, so that
  * prepare_temp_file() does not have to inflate and extract.
  */
-static int reuse_worktree_file(const char *name, const struct object_id *oid, int want_file)
+static int reuse_worktree_file(struct index_state *istate,
+                              const char *name,
+                              const struct object_id *oid,
+                              int want_file)
 {
        const struct cache_entry *ce;
        struct stat st;
@@ -3744,7 +3770,7 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in
         * by diff-cache --cached, which does read the cache before
         * calling us.
         */
-       if (!active_cache)
+       if (!istate->cache)
                return 0;
 
        /* We want to avoid the working directory if our caller
@@ -3763,14 +3789,14 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in
         * Similarly, if we'd have to convert the file contents anyway, that
         * makes the optimization not worthwhile.
         */
-       if (!want_file && would_convert_to_git(&the_index, name))
+       if (!want_file && would_convert_to_git(istate, name))
                return 0;
 
        len = strlen(name);
-       pos = cache_name_pos(name, len);
+       pos = index_name_pos(istate, name, len);
        if (pos < 0)
                return 0;
-       ce = active_cache[pos];
+       ce = istate->cache[pos];
 
        /*
         * This is not the sha1 we are looking for, or
@@ -3790,7 +3816,7 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in
         * If ce matches the file in the work tree, we can reuse it.
         */
        if (ce_uptodate(ce) ||
-           (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
+           (!lstat(name, &st) && !ie_match_stat(istate, ce, &st, 0)))
                return 1;
 
        return 0;
@@ -3823,7 +3849,9 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
  * grab the data for the blob (or file) for our own in-core comparison.
  * diff_filespec has data and size fields for this purpose.
  */
-int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
+int diff_populate_filespec(struct repository *r,
+                          struct diff_filespec *s,
+                          unsigned int flags)
 {
        int size_only = flags & CHECK_SIZE_ONLY;
        int err = 0;
@@ -3850,7 +3878,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
                return diff_populate_gitlink(s, size_only);
 
        if (!s->oid_valid ||
-           reuse_worktree_file(s->path, &s->oid, 0)) {
+           reuse_worktree_file(r->index, s->path, &s->oid, 0)) {
                struct strbuf buf = STRBUF_INIT;
                struct stat st;
                int fd;
@@ -3883,7 +3911,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
                 * point if the path requires us to run the content
                 * conversion.
                 */
-               if (size_only && !would_convert_to_git(&the_index, s->path))
+               if (size_only && !would_convert_to_git(r->index, s->path))
                        return 0;
 
                /*
@@ -3910,7 +3938,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
                /*
                 * Convert from working tree format to canonical git format
                 */
-               if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, conv_flags)) {
+               if (convert_to_git(r->index, s->path, s->data, s->size, &buf, conv_flags)) {
                        size_t size = 0;
                        munmap(s->data, s->size);
                        s->should_munmap = 0;
@@ -3922,8 +3950,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
        else {
                enum object_type type;
                if (size_only || (flags & CHECK_BINARY)) {
-                       type = oid_object_info(the_repository, &s->oid,
-                                              &s->size);
+                       type = oid_object_info(r, &s->oid, &s->size);
                        if (type < 0)
                                die("unable to read %s",
                                    oid_to_hex(&s->oid));
@@ -3961,7 +3988,8 @@ void diff_free_filespec_data(struct diff_filespec *s)
        FREE_AND_NULL(s->cnt_data);
 }
 
-static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
+static void prep_temp_blob(struct index_state *istate,
+                          const char *path, struct diff_tempfile *temp,
                           void *blob,
                           unsigned long size,
                           const struct object_id *oid,
@@ -3979,7 +4007,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
        temp->tempfile = mks_tempfile_ts(tempfile.buf, strlen(base) + 1);
        if (!temp->tempfile)
                die_errno("unable to create temp-file");
-       if (convert_to_working_tree(&the_index, path,
+       if (convert_to_working_tree(istate, path,
                        (const char *)blob, (size_t)size, &buf)) {
                blob = buf.buf;
                size = buf.len;
@@ -3995,8 +4023,9 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
        free(path_dup);
 }
 
-static struct diff_tempfile *prepare_temp_file(const char *name,
-               struct diff_filespec *one)
+static struct diff_tempfile *prepare_temp_file(struct repository *r,
+                                              const char *name,
+                                              struct diff_filespec *one)
 {
        struct diff_tempfile *temp = claim_diff_tempfile();
 
@@ -4013,7 +4042,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
 
        if (!S_ISGITLINK(one->mode) &&
            (!one->oid_valid ||
-            reuse_worktree_file(name, &one->oid, 1))) {
+            reuse_worktree_file(r->index, name, &one->oid, 1))) {
                struct stat st;
                if (lstat(name, &st) < 0) {
                        if (errno == ENOENT)
@@ -4024,7 +4053,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                        struct strbuf sb = STRBUF_INIT;
                        if (strbuf_readlink(&sb, name, st.st_size) < 0)
                                die_errno("readlink(%s)", name);
-                       prep_temp_blob(name, temp, sb.buf, sb.len,
+                       prep_temp_blob(r->index, name, temp, sb.buf, sb.len,
                                       (one->oid_valid ?
                                        &one->oid : &null_oid),
                                       (one->oid_valid ?
@@ -4049,19 +4078,21 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                return temp;
        }
        else {
-               if (diff_populate_filespec(one, 0))
+               if (diff_populate_filespec(r, one, 0))
                        die("cannot read data blob for %s", one->path);
-               prep_temp_blob(name, temp, one->data, one->size,
+               prep_temp_blob(r->index, name, temp,
+                              one->data, one->size,
                               &one->oid, one->mode);
        }
        return temp;
 }
 
-static void add_external_diff_name(struct argv_array *argv,
+static void add_external_diff_name(struct repository *r,
+                                  struct argv_array *argv,
                                   const char *name,
                                   struct diff_filespec *df)
 {
-       struct diff_tempfile *temp = prepare_temp_file(name, df);
+       struct diff_tempfile *temp = prepare_temp_file(r, name, df);
        argv_array_push(argv, temp->name);
        argv_array_push(argv, temp->hex);
        argv_array_push(argv, temp->mode);
@@ -4090,11 +4121,11 @@ static void run_external_diff(const char *pgm,
        argv_array_push(&argv, name);
 
        if (one && two) {
-               add_external_diff_name(&argv, name, one);
+               add_external_diff_name(o->repo, &argv, name, one);
                if (!other)
-                       add_external_diff_name(&argv, name, two);
+                       add_external_diff_name(o->repo, &argv, name, two);
                else {
-                       add_external_diff_name(&argv, other, two);
+                       add_external_diff_name(o->repo, &argv, other, two);
                        argv_array_push(&argv, other);
                        argv_array_push(&argv, xfrm_msg);
                }
@@ -4187,8 +4218,10 @@ static void fill_metainfo(struct strbuf *msg,
 
                if (o->flags.binary) {
                        mmfile_t mf;
-                       if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
-                           (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
+                       if ((!fill_mmfile(o->repo, &mf, one) &&
+                            diff_filespec_is_binary(o->repo, one)) ||
+                           (!fill_mmfile(o->repo, &mf, two) &&
+                            diff_filespec_is_binary(o->repo, two)))
                                abbrev = hexsz;
                }
                strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set,
@@ -4216,7 +4249,9 @@ static void run_diff_cmd(const char *pgm,
 
 
        if (o->flags.allow_external) {
-               struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
+               struct userdiff_driver *drv;
+
+               drv = userdiff_find_by_path(o->repo->index, attr_path);
                if (drv && drv->external)
                        pgm = drv->external;
        }
@@ -4245,7 +4280,7 @@ static void run_diff_cmd(const char *pgm,
                fprintf(o->file, "* Unmerged path %s\n", name);
 }
 
-static void diff_fill_oid_info(struct diff_filespec *one)
+static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *istate)
 {
        if (DIFF_FILE_VALID(one)) {
                if (!one->oid_valid) {
@@ -4256,7 +4291,7 @@ static void diff_fill_oid_info(struct diff_filespec *one)
                        }
                        if (lstat(one->path, &st) < 0)
                                die_errno("stat '%s'", one->path);
-                       if (index_path(&one->oid, one->path, &st, 0))
+                       if (index_path(istate, &one->oid, one->path, &st, 0))
                                die("cannot hash %s", one->path);
                }
        }
@@ -4267,12 +4302,12 @@ static void diff_fill_oid_info(struct diff_filespec *one)
 static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
 {
        /* Strip the prefix but do not molest /dev/null and absolute paths */
-       if (*namep && **namep != '/') {
+       if (*namep && !is_absolute_path(*namep)) {
                *namep += prefix_length;
                if (**namep == '/')
                        ++*namep;
        }
-       if (*otherp && **otherp != '/') {
+       if (*otherp && !is_absolute_path(*otherp)) {
                *otherp += prefix_length;
                if (**otherp == '/')
                        ++*otherp;
@@ -4304,8 +4339,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
                return;
        }
 
-       diff_fill_oid_info(one);
-       diff_fill_oid_info(two);
+       diff_fill_oid_info(one, o->repo->index);
+       diff_fill_oid_info(two, o->repo->index);
 
        if (!pgm &&
            DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
@@ -4316,7 +4351,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
                 */
                struct diff_filespec *null = alloc_filespec(two->path);
                run_diff_cmd(NULL, name, other, attr_path,
-                            one, null, &msg, o, p);
+                            one, null, &msg,
+                            o, p);
                free(null);
                strbuf_release(&msg);
 
@@ -4340,7 +4376,8 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
 
        if (DIFF_PAIR_UNMERGED(p)) {
                /* unmerged */
-               builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, o, p);
+               builtin_diffstat(p->one->path, NULL, NULL, NULL,
+                                diffstat, o, p);
                return;
        }
 
@@ -4350,10 +4387,11 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
        if (o->prefix_length)
                strip_prefix(o->prefix_length, &name, &other);
 
-       diff_fill_oid_info(p->one);
-       diff_fill_oid_info(p->two);
+       diff_fill_oid_info(p->one, o->repo->index);
+       diff_fill_oid_info(p->two, o->repo->index);
 
-       builtin_diffstat(name, other, p->one, p->two, diffstat, o, p);
+       builtin_diffstat(name, other, p->one, p->two,
+                        diffstat, o, p);
 }
 
 static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
@@ -4374,17 +4412,18 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
        if (o->prefix_length)
                strip_prefix(o->prefix_length, &name, &other);
 
-       diff_fill_oid_info(p->one);
-       diff_fill_oid_info(p->two);
+       diff_fill_oid_info(p->one, o->repo->index);
+       diff_fill_oid_info(p->two, o->repo->index);
 
        builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
 }
 
-void diff_setup(struct diff_options *options)
+void repo_diff_setup(struct repository *r, struct diff_options *options)
 {
        memcpy(options, &default_diff_options, sizeof(*options));
 
        options->file = stdout;
+       options->repo = r;
 
        options->output_indicators[OUTPUT_INDICATOR_NEW] = '+';
        options->output_indicators[OUTPUT_INDICATOR_OLD] = '-';
@@ -5683,8 +5722,8 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
                if (DIFF_PAIR_UNMERGED(p))
                        continue;
 
-               diff_fill_oid_info(p->one);
-               diff_fill_oid_info(p->two);
+               diff_fill_oid_info(p->one, options->repo->index);
+               diff_fill_oid_info(p->two, options->repo->index);
 
                len1 = remove_space(p->one->path, strlen(p->one->path));
                len2 = remove_space(p->two->path, strlen(p->two->path));
@@ -5716,12 +5755,12 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
                if (diff_header_only)
                        continue;
 
-               if (fill_mmfile(&mf1, p->one) < 0 ||
-                   fill_mmfile(&mf2, p->two) < 0)
+               if (fill_mmfile(options->repo, &mf1, p->one) < 0 ||
+                   fill_mmfile(options->repo, &mf2, p->two) < 0)
                        return error("unable to read files to diff");
 
-               if (diff_filespec_is_binary(p->one) ||
-                   diff_filespec_is_binary(p->two)) {
+               if (diff_filespec_is_binary(options->repo, p->one) ||
+                   diff_filespec_is_binary(options->repo, p->two)) {
                        git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
                                        GIT_SHA1_HEXSZ);
                        git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
@@ -5839,8 +5878,8 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
                        if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
                                dim_moved_lines(o);
 
-                       hashmap_free(&add_lines, 0);
-                       hashmap_free(&del_lines, 0);
+                       hashmap_free(&add_lines, 1);
+                       hashmap_free(&del_lines, 1);
                }
 
                for (i = 0; i < esm.nr; i++)
@@ -6024,19 +6063,21 @@ static void diffcore_apply_filter(struct diff_options *options)
 }
 
 /* Check whether two filespecs with the same mode and size are identical */
-static int diff_filespec_is_identical(struct diff_filespec *one,
+static int diff_filespec_is_identical(struct repository *r,
+                                     struct diff_filespec *one,
                                      struct diff_filespec *two)
 {
        if (S_ISGITLINK(one->mode))
                return 0;
-       if (diff_populate_filespec(one, 0))
+       if (diff_populate_filespec(r, one, 0))
                return 0;
-       if (diff_populate_filespec(two, 0))
+       if (diff_populate_filespec(r, two, 0))
                return 0;
        return !memcmp(one->data, two->data, one->size);
 }
 
-static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
+static int diff_filespec_check_stat_unmatch(struct repository *r,
+                                           struct diff_filepair *p)
 {
        if (p->done_skip_stat_unmatch)
                return p->skip_stat_unmatch_result;
@@ -6060,10 +6101,10 @@ static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
            !DIFF_FILE_VALID(p->two) ||
            (p->one->oid_valid && p->two->oid_valid) ||
            (p->one->mode != p->two->mode) ||
-           diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
-           diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
+           diff_populate_filespec(r, p->one, CHECK_SIZE_ONLY) ||
+           diff_populate_filespec(r, p->two, CHECK_SIZE_ONLY) ||
            (p->one->size != p->two->size) ||
-           !diff_filespec_is_identical(p->one, p->two)) /* (2) */
+           !diff_filespec_is_identical(r, p->one, p->two)) /* (2) */
                p->skip_stat_unmatch_result = 1;
        return p->skip_stat_unmatch_result;
 }
@@ -6078,7 +6119,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
 
-               if (diff_filespec_check_stat_unmatch(p))
+               if (diff_filespec_check_stat_unmatch(diffopt->repo, p))
                        diff_q(&outq, p);
                else {
                        /*
@@ -6120,7 +6161,8 @@ void diffcore_std(struct diff_options *options)
        if (!options->found_follow) {
                /* See try_to_follow_renames() in tree-diff.c */
                if (options->break_opt != -1)
-                       diffcore_break(options->break_opt);
+                       diffcore_break(options->repo,
+                                      options->break_opt);
                if (options->detect_rename)
                        diffcore_rename(options);
                if (options->break_opt != -1)
@@ -6271,7 +6313,7 @@ void diff_change(struct diff_options *options,
                return;
 
        if (options->flags.quick && options->skip_stat_unmatch &&
-           !diff_filespec_check_stat_unmatch(p))
+           !diff_filespec_check_stat_unmatch(options->repo, p))
                return;
 
        options->flags.has_changes = 1;
@@ -6293,8 +6335,10 @@ struct diff_filepair *diff_unmerge(struct diff_options *options, const char *pat
        return pair;
 }
 
-static char *run_textconv(const char *pgm, struct diff_filespec *spec,
-               size_t *outsize)
+static char *run_textconv(struct repository *r,
+                         const char *pgm,
+                         struct diff_filespec *spec,
+                         size_t *outsize)
 {
        struct diff_tempfile *temp;
        const char *argv[3];
@@ -6303,7 +6347,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
        struct strbuf buf = STRBUF_INIT;
        int err = 0;
 
-       temp = prepare_temp_file(spec->path, spec);
+       temp = prepare_temp_file(r, spec->path, spec);
        *arg++ = pgm;
        *arg++ = temp->name;
        *arg = NULL;
@@ -6330,7 +6374,8 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
        return strbuf_detach(&buf, outsize);
 }
 
-size_t fill_textconv(struct userdiff_driver *driver,
+size_t fill_textconv(struct repository *r,
+                    struct userdiff_driver *driver,
                     struct diff_filespec *df,
                     char **outbuf)
 {
@@ -6341,7 +6386,7 @@ size_t fill_textconv(struct userdiff_driver *driver,
                        *outbuf = "";
                        return 0;
                }
-               if (diff_populate_filespec(df, 0))
+               if (diff_populate_filespec(r, df, 0))
                        die("unable to read files to diff");
                *outbuf = df->data;
                return df->size;
@@ -6358,7 +6403,7 @@ size_t fill_textconv(struct userdiff_driver *driver,
                        return size;
        }
 
-       *outbuf = run_textconv(driver->textconv, df, &size);
+       *outbuf = run_textconv(r, driver->textconv, df, &size);
        if (!*outbuf)
                die("unable to read files to diff");
 
@@ -6378,7 +6423,8 @@ size_t fill_textconv(struct userdiff_driver *driver,
        return size;
 }
 
-int textconv_object(const char *path,
+int textconv_object(struct repository *r,
+                   const char *path,
                    unsigned mode,
                    const struct object_id *oid,
                    int oid_valid,
@@ -6390,13 +6436,13 @@ int textconv_object(const char *path,
 
        df = alloc_filespec(path);
        fill_filespec(df, oid, oid_valid, mode);
-       textconv = get_textconv(df);
+       textconv = get_textconv(r->index, df);
        if (!textconv) {
                free_filespec(df);
                return 0;
        }
 
-       *buf_size = fill_textconv(textconv, df, buf);
+       *buf_size = fill_textconv(r, textconv, df, buf);
        free_filespec(df);
        return 1;
 }
diff --git a/diff.h b/diff.h
index a30cc35ec3b4cb525340691a39ca88e89e379e4c..ce5e8a8183e848b2e36d50bea271f687a450e3af 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -18,6 +18,7 @@ struct userdiff_driver;
 struct oid_array;
 struct commit;
 struct combine_diff_path;
+struct repository;
 
 typedef int (*pathchange_fn_t)(struct diff_options *options,
                 struct combine_diff_path *path);
@@ -225,6 +226,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;
+
+       struct repository *repo;
 };
 
 void diff_emit_submodule_del(struct diff_options *o, const char *line);
@@ -338,7 +341,10 @@ int git_diff_basic_config(const char *var, const char *value, void *cb);
 int git_diff_heuristic_config(const char *var, const char *value, void *cb);
 void init_diff_ui_defaults(void);
 int git_diff_ui_config(const char *var, const char *value, void *cb);
-void diff_setup(struct diff_options *);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define diff_setup(diffopts) repo_diff_setup(the_repository, diffopts)
+#endif
+void repo_diff_setup(struct repository *, struct diff_options *);
 int diff_opt_parse(struct diff_options *, const char **, int, const char *);
 void diff_setup_done(struct diff_options *);
 int git_config_rename(const char *var, const char *value);
@@ -428,7 +434,7 @@ int diff_flush_patch_id(struct diff_options *, struct object_id *, int);
 
 int diff_result_code(struct diff_options *, int);
 
-void diff_no_index(struct rev_info *, int, const char **);
+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 ita_invisible_in_index);
@@ -444,7 +450,8 @@ int index_differs_from(const char *def, const struct diff_flags *flags,
  * struct. If it is non-NULL, then "outbuf" points to a newly allocated buffer
  * that should be freed by the caller.
  */
-size_t fill_textconv(struct userdiff_driver *driver,
+size_t fill_textconv(struct repository *r,
+                    struct userdiff_driver *driver,
                     struct diff_filespec *df,
                     char **outbuf);
 
@@ -453,14 +460,19 @@ size_t fill_textconv(struct userdiff_driver *driver,
  * and only if it has textconv enabled (otherwise return NULL). The result
  * can be passed to fill_textconv().
  */
-struct userdiff_driver *get_textconv(struct diff_filespec *one);
+struct userdiff_driver *get_textconv(struct index_state *istate,
+                                    struct diff_filespec *one);
 
 /*
  * Prepare diff_filespec and convert it using diff textconv API
  * if the textconv driver exists.
  * Return 1 if the conversion succeeds, 0 otherwise.
  */
-int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);
+int textconv_object(struct repository *repo,
+                   const char *path,
+                   unsigned mode,
+                   const struct object_id *oid, int oid_valid,
+                   char **buf, unsigned long *buf_size);
 
 int parse_rename_score(const char **cp_p);
 
index e11fcfdb391425ac9efaea829d4756891dcb941e..875aefd3febf46771cc56c1f2b9f26318cfc732b 100644 (file)
@@ -5,7 +5,8 @@
 #include "diff.h"
 #include "diffcore.h"
 
-static int should_break(struct diff_filespec *src,
+static int should_break(struct repository *r,
+                       struct diff_filespec *src,
                        struct diff_filespec *dst,
                        int break_score,
                        int *merge_score_p)
@@ -61,7 +62,8 @@ static int should_break(struct diff_filespec *src,
            oideq(&src->oid, &dst->oid))
                return 0; /* they are the same */
 
-       if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
+       if (diff_populate_filespec(r, src, 0) ||
+           diff_populate_filespec(r, dst, 0))
                return 0; /* error but caught downstream */
 
        max_size = ((src->size > dst->size) ? src->size : dst->size);
@@ -71,7 +73,7 @@ static int should_break(struct diff_filespec *src,
        if (!src->size)
                return 0; /* we do not let empty files get renamed */
 
-       if (diffcore_count_changes(src, dst,
+       if (diffcore_count_changes(r, src, dst,
                                   &src->cnt_data, &dst->cnt_data,
                                   &src_copied, &literal_added))
                return 0;
@@ -114,7 +116,7 @@ static int should_break(struct diff_filespec *src,
        return 1;
 }
 
-void diffcore_break(int break_score)
+void diffcore_break(struct repository *r, int break_score)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        struct diff_queue_struct outq;
@@ -178,7 +180,7 @@ void diffcore_break(int break_score)
                    object_type(p->one->mode) == OBJ_BLOB &&
                    object_type(p->two->mode) == OBJ_BLOB &&
                    !strcmp(p->one->path, p->two->path)) {
-                       if (should_break(p->one, p->two,
+                       if (should_break(r, p->one, p->two,
                                         break_score, &score)) {
                                /* Split this into delete and create */
                                struct diff_filespec *null_one, *null_two;
index c83d45a0470d412c29f8c392e84bb010150dbb2a..5668ace60d6012d1b91e5359975aaec0df8d5b34 100644 (file)
@@ -121,14 +121,15 @@ static int spanhash_cmp(const void *a_, const void *b_)
                a->hashval > b->hashval ? 1 : 0;
 }
 
-static struct spanhash_top *hash_chars(struct diff_filespec *one)
+static struct spanhash_top *hash_chars(struct repository *r,
+                                      struct diff_filespec *one)
 {
        int i, n;
        unsigned int accum1, accum2, hashval;
        struct spanhash_top *hash;
        unsigned char *buf = one->data;
        unsigned int sz = one->size;
-       int is_text = !diff_filespec_is_binary(one);
+       int is_text = !diff_filespec_is_binary(r, one);
 
        i = INITIAL_HASH_SIZE;
        hash = xmalloc(st_add(sizeof(*hash),
@@ -162,7 +163,8 @@ static struct spanhash_top *hash_chars(struct diff_filespec *one)
        return hash;
 }
 
-int diffcore_count_changes(struct diff_filespec *src,
+int diffcore_count_changes(struct repository *r,
+                          struct diff_filespec *src,
                           struct diff_filespec *dst,
                           void **src_count_p,
                           void **dst_count_p,
@@ -177,14 +179,14 @@ int diffcore_count_changes(struct diff_filespec *src,
        if (src_count_p)
                src_count = *src_count_p;
        if (!src_count) {
-               src_count = hash_chars(src);
+               src_count = hash_chars(r, src);
                if (src_count_p)
                        *src_count_p = src_count;
        }
        if (dst_count_p)
                dst_count = *dst_count_p;
        if (!dst_count) {
-               dst_count = hash_chars(dst);
+               dst_count = hash_chars(r, dst);
                if (dst_count_p)
                        *dst_count_p = dst_count;
        }
index 800a899c8621eaeba7f4246139df0f289ceb6ee2..d2361e06a1e45a1bdb94d6f5d6d2c003ac587e30 100644 (file)
@@ -139,8 +139,8 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
                return 0;
 
        if (o->flags.allow_textconv) {
-               textconv_one = get_textconv(p->one);
-               textconv_two = get_textconv(p->two);
+               textconv_one = get_textconv(o->repo->index, p->one);
+               textconv_two = get_textconv(o->repo->index, p->two);
        }
 
        /*
@@ -153,8 +153,8 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
        if (textconv_one == textconv_two && diff_unmodified_pair(p))
                return 0;
 
-       mf1.size = fill_textconv(textconv_one, p->one, &mf1.ptr);
-       mf2.size = fill_textconv(textconv_two, p->two, &mf2.ptr);
+       mf1.size = fill_textconv(o->repo, textconv_one, p->one, &mf1.ptr);
+       mf2.size = fill_textconv(o->repo, textconv_two, p->two, &mf2.ptr);
 
        ret = fn(DIFF_FILE_VALID(p->one) ? &mf1 : NULL,
                 DIFF_FILE_VALID(p->two) ? &mf2 : NULL,
index daddd9b28af1e20fe82aefa654bad04f4b52c1d3..07bd34b63145e1dc179afebcc7af351c34c2ab07 100644 (file)
@@ -128,7 +128,8 @@ struct diff_score {
        short name_score;
 };
 
-static int estimate_similarity(struct diff_filespec *src,
+static int estimate_similarity(struct repository *r,
+                              struct diff_filespec *src,
                               struct diff_filespec *dst,
                               int minimum_score)
 {
@@ -165,10 +166,10 @@ static int estimate_similarity(struct diff_filespec *src,
         * say whether the size is valid or not!)
         */
        if (!src->cnt_data &&
-           diff_populate_filespec(src, CHECK_SIZE_ONLY))
+           diff_populate_filespec(r, src, CHECK_SIZE_ONLY))
                return 0;
        if (!dst->cnt_data &&
-           diff_populate_filespec(dst, CHECK_SIZE_ONLY))
+           diff_populate_filespec(r, dst, CHECK_SIZE_ONLY))
                return 0;
 
        max_size = ((src->size > dst->size) ? src->size : dst->size);
@@ -186,12 +187,12 @@ static int estimate_similarity(struct diff_filespec *src,
        if (max_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
                return 0;
 
-       if (!src->cnt_data && diff_populate_filespec(src, 0))
+       if (!src->cnt_data && diff_populate_filespec(r, src, 0))
                return 0;
-       if (!dst->cnt_data && diff_populate_filespec(dst, 0))
+       if (!dst->cnt_data && diff_populate_filespec(r, dst, 0))
                return 0;
 
-       if (diffcore_count_changes(src, dst,
+       if (diffcore_count_changes(r, src, dst,
                                   &src->cnt_data, &dst->cnt_data,
                                   &src_copied, &literal_added))
                return 0;
@@ -256,10 +257,11 @@ struct file_similarity {
        struct diff_filespec *filespec;
 };
 
-static unsigned int hash_filespec(struct diff_filespec *filespec)
+static unsigned int hash_filespec(struct repository *r,
+                                 struct diff_filespec *filespec)
 {
        if (!filespec->oid_valid) {
-               if (diff_populate_filespec(filespec, 0))
+               if (diff_populate_filespec(r, filespec, 0))
                        return 0;
                hash_object_file(filespec->data, filespec->size, "blob",
                                 &filespec->oid);
@@ -280,7 +282,9 @@ static int find_identical_files(struct hashmap *srcs,
        /*
         * Find the best source match for specified destination.
         */
-       p = hashmap_get_from_hash(srcs, hash_filespec(target), NULL);
+       p = hashmap_get_from_hash(srcs,
+                                 hash_filespec(options->repo, target),
+                                 NULL);
        for (; p; p = hashmap_get_next(srcs, p)) {
                int score;
                struct diff_filespec *source = p->filespec;
@@ -316,14 +320,16 @@ static int find_identical_files(struct hashmap *srcs,
        return renames;
 }
 
-static void insert_file_table(struct hashmap *table, int index, struct diff_filespec *filespec)
+static void insert_file_table(struct repository *r,
+                             struct hashmap *table, int index,
+                             struct diff_filespec *filespec)
 {
        struct file_similarity *entry = xmalloc(sizeof(*entry));
 
        entry->index = index;
        entry->filespec = filespec;
 
-       hashmap_entry_init(entry, hash_filespec(filespec));
+       hashmap_entry_init(entry, hash_filespec(r, filespec));
        hashmap_add(table, entry);
 }
 
@@ -344,7 +350,9 @@ static int find_exact_renames(struct diff_options *options)
         */
        hashmap_init(&file_table, NULL, NULL, rename_src_nr);
        for (i = rename_src_nr-1; i >= 0; i--)
-               insert_file_table(&file_table, i, rename_src[i].p->one);
+               insert_file_table(options->repo,
+                                 &file_table, i,
+                                 rename_src[i].p->one);
 
        /* Walk the destinations and find best source match */
        for (i = 0; i < rename_dst_nr; i++)
@@ -557,7 +565,8 @@ void diffcore_rename(struct diff_options *options)
                            diff_unmodified_pair(rename_src[j].p))
                                continue;
 
-                       this_src.score = estimate_similarity(one, two,
+                       this_src.score = estimate_similarity(options->repo,
+                                                            one, two,
                                                             minimum_score);
                        this_src.name_score = basename_same(one, two);
                        this_src.dst = i;
index 8d81a45f510c0aed0cafcd3dce638f18fc6566f7..b651061c0e3a40c0821fe548dbb203e911e305ad 100644 (file)
@@ -7,6 +7,8 @@
 #include "cache.h"
 
 struct diff_options;
+struct repository;
+struct userdiff_driver;
 
 /* This header file is internal between diff.c and its diff transformers
  * (e.g. diffcore-rename, diffcore-pickaxe).  Never include this header
@@ -26,8 +28,6 @@ struct diff_options;
 
 #define MINIMUM_BREAK_SIZE     400 /* do not break a file smaller than this */
 
-struct userdiff_driver;
-
 struct diff_filespec {
        struct object_id oid;
        char *path;
@@ -61,10 +61,10 @@ void fill_filespec(struct diff_filespec *, const struct object_id *,
 
 #define CHECK_SIZE_ONLY 1
 #define CHECK_BINARY    2
-int diff_populate_filespec(struct diff_filespec *, unsigned int);
+int diff_populate_filespec(struct repository *, struct diff_filespec *, unsigned int);
 void diff_free_filespec_data(struct diff_filespec *);
 void diff_free_filespec_blob(struct diff_filespec *);
-int diff_filespec_is_binary(struct diff_filespec *);
+int diff_filespec_is_binary(struct repository *, struct diff_filespec *);
 
 struct diff_filepair {
        struct diff_filespec *one;
@@ -111,7 +111,7 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *,
                                 struct diff_filespec *);
 void diff_q(struct diff_queue_struct *, struct diff_filepair *);
 
-void diffcore_break(int);
+void diffcore_break(struct repository *, int);
 void diffcore_rename(struct diff_options *);
 void diffcore_merge_broken(void);
 void diffcore_pickaxe(struct diff_options *);
@@ -142,7 +142,8 @@ void diff_debug_queue(const char *, struct diff_queue_struct *);
 #define diff_debug_queue(a,b) do { /* nothing */ } while (0)
 #endif
 
-int diffcore_count_changes(struct diff_filespec *src,
+int diffcore_count_changes(struct repository *r,
+                          struct diff_filespec *src,
                           struct diff_filespec *dst,
                           void **src_count_p,
                           void **dst_count_p,
index 9a9b4e12d1db7f8ec2f4f99d9a5f348b680cecc3..c985eee1f9d1c7307b7f533cb2d38de5a42ccb23 100644 (file)
--- a/editor.c
+++ b/editor.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "config.h"
 #include "strbuf.h"
 #include "run-command.h"
 #include "sigchain.h"
@@ -34,10 +35,21 @@ const char *git_editor(void)
        return editor;
 }
 
-int launch_editor(const char *path, struct strbuf *buffer, const char *const *env)
+const char *git_sequence_editor(void)
 {
-       const char *editor = git_editor();
+       const char *editor = getenv("GIT_SEQUENCE_EDITOR");
+
+       if (!editor)
+               git_config_get_string_const("sequence.editor", &editor);
+       if (!editor)
+               editor = git_editor();
 
+       return editor;
+}
+
+static int launch_specified_editor(const char *editor, const char *path,
+                                  struct strbuf *buffer, const char *const *env)
+{
        if (!editor)
                return error("Terminal is dumb, but EDITOR unset");
 
@@ -95,3 +107,14 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en
                return error_errno("could not read file '%s'", path);
        return 0;
 }
+
+int launch_editor(const char *path, struct strbuf *buffer, const char *const *env)
+{
+       return launch_specified_editor(git_editor(), path, buffer, env);
+}
+
+int launch_sequence_editor(const char *path, struct strbuf *buffer,
+                          const char *const *env)
+{
+       return launch_specified_editor(git_sequence_editor(), path, buffer, env);
+}
index ddb44a22dce42b5cce8f747987e0848483350993..9e3967ce6626be459ad7d8a0590240b06e7056e3 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef FETCH_NEGOTIATOR
-#define FETCH_NEGOTIATOR
+#ifndef FETCH_NEGOTIATOR_H
+#define FETCH_NEGOTIATOR_H
 
 struct commit;
 
index 75047a4b2a491e805f7c500dc804e78a8538bfa2..b3ed7121bc86805e87e909ff995eb8b439a2f995 100644 (file)
@@ -76,8 +76,7 @@ struct alternate_object_cache {
        size_t nr, alloc;
 };
 
-static void cache_one_alternate(const char *refname,
-                               const struct object_id *oid,
+static void cache_one_alternate(const struct object_id *oid,
                                void *vcache)
 {
        struct alternate_object_cache *cache = vcache;
@@ -253,8 +252,10 @@ static int find_common(struct fetch_negotiator *negotiator,
        if (args->stateless_rpc && multi_ack == 1)
                die(_("--stateless-rpc requires multi_ack_detailed"));
 
-       mark_tips(negotiator, args->negotiation_tips);
-       for_each_cached_alternate(negotiator, insert_one_alternate_object);
+       if (!args->no_dependents) {
+               mark_tips(negotiator, args->negotiation_tips);
+               for_each_cached_alternate(negotiator, insert_one_alternate_object);
+       }
 
        fetching = 0;
        for ( ; refs ; refs = refs->next) {
@@ -271,8 +272,12 @@ static int find_common(struct fetch_negotiator *negotiator,
                 * We use lookup_object here because we are only
                 * interested in the case we *know* the object is
                 * reachable and we have already scanned it.
+                *
+                * Do this only if args->no_dependents is false (if it is true,
+                * we cannot trust the object flags).
                 */
-               if (((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+               if (!args->no_dependents &&
+                   ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
                                (o->flags & COMPLETE)) {
                        continue;
                }
@@ -526,21 +531,14 @@ static void add_refs_to_oidset(struct oidset *oids, struct ref *refs)
                oidset_insert(oids, &refs->old_oid);
 }
 
-static int tip_oids_contain(struct oidset *tip_oids,
-                           struct ref *unmatched, struct ref *newlist,
-                           const struct object_id *id)
+static int is_unmatched_ref(const struct ref *ref)
 {
-       /*
-        * Note that this only looks at the ref lists the first time it's
-        * called. This works out in filter_refs() because even though it may
-        * add to "newlist" between calls, the additions will always be for
-        * oids that are already in the set.
-        */
-       if (!tip_oids->map.map.tablesize) {
-               add_refs_to_oidset(tip_oids, unmatched);
-               add_refs_to_oidset(tip_oids, newlist);
-       }
-       return oidset_contains(tip_oids, id);
+       struct object_id oid;
+       const char *p;
+       return  ref->match_status == REF_NOT_MATCHED &&
+               !parse_oid_hex(ref->name, &oid, &p) &&
+               *p == '\0' &&
+               oideq(&oid, &ref->old_oid);
 }
 
 static void filter_refs(struct fetch_pack_args *args,
@@ -553,6 +551,8 @@ static void filter_refs(struct fetch_pack_args *args,
        struct ref *ref, *next;
        struct oidset tip_oids = OIDSET_INIT;
        int i;
+       int strict = !(allow_unadvertised_object_request &
+                      (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
 
        i = 0;
        for (ref = *refs; ref; ref = next) {
@@ -589,23 +589,25 @@ static void filter_refs(struct fetch_pack_args *args,
                }
        }
 
+       if (strict) {
+               for (i = 0; i < nr_sought; i++) {
+                       ref = sought[i];
+                       if (!is_unmatched_ref(ref))
+                               continue;
+
+                       add_refs_to_oidset(&tip_oids, unmatched);
+                       add_refs_to_oidset(&tip_oids, newlist);
+                       break;
+               }
+       }
+
        /* Append unmatched requests to the list */
        for (i = 0; i < nr_sought; i++) {
-               struct object_id oid;
-               const char *p;
-
                ref = sought[i];
-               if (ref->match_status != REF_NOT_MATCHED)
-                       continue;
-               if (parse_oid_hex(ref->name, &oid, &p) ||
-                   *p != '\0' ||
-                   !oideq(&oid, &ref->old_oid))
+               if (!is_unmatched_ref(ref))
                        continue;
 
-               if ((allow_unadvertised_object_request &
-                    (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1)) ||
-                   tip_oids_contain(&tip_oids, unmatched, newlist,
-                                    &ref->old_oid)) {
+               if (!strict || oidset_contains(&tip_oids, &ref->old_oid)) {
                        ref->match_status = REF_MATCHED;
                        *newtail = copy_ref(ref);
                        newtail = &(*newtail)->next;
@@ -710,31 +712,29 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
 
        oidset_clear(&loose_oid_set);
 
-       if (!args->no_dependents) {
-               if (!args->deepen) {
-                       for_each_ref(mark_complete_oid, NULL);
-                       for_each_cached_alternate(NULL, mark_alternate_complete);
-                       commit_list_sort_by_date(&complete);
-                       if (cutoff)
-                               mark_recent_complete_commits(args, cutoff);
-               }
+       if (!args->deepen) {
+               for_each_ref(mark_complete_oid, NULL);
+               for_each_cached_alternate(NULL, mark_alternate_complete);
+               commit_list_sort_by_date(&complete);
+               if (cutoff)
+                       mark_recent_complete_commits(args, cutoff);
+       }
 
-               /*
-                * Mark all complete remote refs as common refs.
-                * Don't mark them common yet; the server has to be told so first.
-                */
-               for (ref = *refs; ref; ref = ref->next) {
-                       struct object *o = deref_tag(the_repository,
-                                                    lookup_object(the_repository,
-                                                    ref->old_oid.hash),
-                                                    NULL, 0);
+       /*
+        * Mark all complete remote refs as common refs.
+        * Don't mark them common yet; the server has to be told so first.
+        */
+       for (ref = *refs; ref; ref = ref->next) {
+               struct object *o = deref_tag(the_repository,
+                                            lookup_object(the_repository,
+                                            ref->old_oid.hash),
+                                            NULL, 0);
 
-                       if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
-                               continue;
+               if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
+                       continue;
 
-                       negotiator->known_common(negotiator,
-                                                (struct commit *)o);
-               }
+               negotiator->known_common(negotiator,
+                                        (struct commit *)o);
        }
 
        save_commit_buffer = old_save_commit_buffer;
@@ -990,11 +990,15 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        if (!server_supports("deepen-relative") && args->deepen_relative)
                die(_("Server does not support --deepen"));
 
-       mark_complete_and_common_ref(&negotiator, args, &ref);
-       filter_refs(args, &ref, sought, nr_sought);
-       if (everything_local(args, &ref)) {
-               packet_flush(fd[1]);
-               goto all_done;
+       if (!args->no_dependents) {
+               mark_complete_and_common_ref(&negotiator, args, &ref);
+               filter_refs(args, &ref, sought, nr_sought);
+               if (everything_local(args, &ref)) {
+                       packet_flush(fd[1]);
+                       goto all_done;
+               }
+       } else {
+               filter_refs(args, &ref, sought, nr_sought);
        }
        if (find_common(&negotiator, args, fd, &oid, ref) < 0)
                if (!args->keep_pack)
@@ -1040,7 +1044,7 @@ static void add_shallow_requests(struct strbuf *req_buf,
        }
 }
 
-static void add_wants(const struct ref *wants, struct strbuf *req_buf)
+static void add_wants(int no_dependents, const struct ref *wants, struct strbuf *req_buf)
 {
        int use_ref_in_want = server_supports_feature("fetch", "ref-in-want", 0);
 
@@ -1057,8 +1061,12 @@ static void add_wants(const struct ref *wants, struct strbuf *req_buf)
                 * We use lookup_object here because we are only
                 * interested in the case we *know* the object is
                 * reachable and we have already scanned it.
+                *
+                * Do this only if args->no_dependents is false (if it is true,
+                * we cannot trust the object flags).
                 */
-               if (((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+               if (!no_dependents &&
+                   ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
                    (o->flags & COMPLETE)) {
                        continue;
                }
@@ -1155,7 +1163,7 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
        }
 
        /* add wants */
-       add_wants(wants, &req_buf);
+       add_wants(args->no_dependents, wants, &req_buf);
 
        if (args->no_dependents) {
                packet_buf_write(&req_buf, "done");
@@ -1346,16 +1354,21 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                                args->deepen = 1;
 
                        /* Filter 'ref' by 'sought' and those that aren't local */
-                       mark_complete_and_common_ref(&negotiator, args, &ref);
-                       filter_refs(args, &ref, sought, nr_sought);
-                       if (everything_local(args, &ref))
-                               state = FETCH_DONE;
-                       else
+                       if (!args->no_dependents) {
+                               mark_complete_and_common_ref(&negotiator, args, &ref);
+                               filter_refs(args, &ref, sought, nr_sought);
+                               if (everything_local(args, &ref))
+                                       state = FETCH_DONE;
+                               else
+                                       state = FETCH_SEND_REQUEST;
+
+                               mark_tips(&negotiator, args->negotiation_tips);
+                               for_each_cached_alternate(&negotiator,
+                                                         insert_one_alternate_object);
+                       } else {
+                               filter_refs(args, &ref, sought, nr_sought);
                                state = FETCH_SEND_REQUEST;
-
-                       mark_tips(&negotiator, args->negotiation_tips);
-                       for_each_cached_alternate(&negotiator,
-                                                 insert_one_alternate_object);
+                       }
                        break;
                case FETCH_SEND_REQUEST:
                        if (send_fetch_request(&negotiator, fd[1], args, ref,
@@ -1598,7 +1611,21 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
        if (nr_sought)
                nr_sought = remove_duplicates_in_refs(sought, nr_sought);
 
-       if (!ref) {
+       if (args->no_dependents && !args->filter_options.choice) {
+               /*
+                * The protocol does not support requesting that only the
+                * wanted objects be sent, so approximate this by setting a
+                * "blob:none" filter if no filter is already set. This works
+                * for all object types: note that wanted blobs will still be
+                * sent because they are directly specified as a "want".
+                *
+                * NEEDSWORK: Add an option in the protocol to request that
+                * only the wanted objects be sent, and implement it.
+                */
+               parse_list_objects_filter(&args->filter_options, "blob:none");
+       }
+
+       if (version != protocol_v2 && !ref) {
                packet_flush(fd[1]);
                die(_("no matching remote head"));
        }
index 5b6e868802b53ca5fa59eb864199274ac538c242..43ec344d95b40c701b2fc703b4b2b1e3ff72a2d2 100644 (file)
@@ -43,6 +43,13 @@ struct fetch_pack_args {
        unsigned from_promisor:1;
 
        /*
+        * Attempt to fetch only the wanted objects, and not any objects
+        * referred to by them. Due to protocol limitations, extraneous
+        * objects may still be included. (When fetching non-blob
+        * objects, only blobs are excluded; when fetching a blob, the
+        * blob itself will still be sent. The client does not need to
+        * know whether a wanted object is a blob or not.)
+        *
         * If 1, fetch_pack() will also not modify any object flags.
         * This allows fetch_pack() to safely be called by any function,
         * regardless of which object flags it uses (if any).
diff --git a/fsck.c b/fsck.c
index 38624d251126ed5a75cd6b8e8b7adfddbb29b283..68502ce85b11bf0ef4445f2b1688b3834301ba53 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -479,7 +479,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
                if (name) {
                        struct object *obj = &parents->item->object;
 
-                       if (++counter > 1)
+                       if (counter++)
                                put_object_name(options, obj, "%s^%d",
                                        name, counter);
                        else if (generation > 0)
diff --git a/fuzz-pack-headers.c b/fuzz-pack-headers.c
new file mode 100644 (file)
index 0000000..99da1d0
--- /dev/null
@@ -0,0 +1,14 @@
+#include "packfile.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       enum object_type type;
+       unsigned long len;
+
+       unpack_object_header_buffer((const unsigned char *)data,
+                                   (unsigned long)size, &type, &len);
+
+       return 0;
+}
diff --git a/fuzz-pack-idx.c b/fuzz-pack-idx.c
new file mode 100644 (file)
index 0000000..0c3d777
--- /dev/null
@@ -0,0 +1,13 @@
+#include "object-store.h"
+#include "packfile.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       struct packed_git p;
+
+       load_idx("fuzz-input", GIT_SHA1_RAWSZ, (void *)data, size, &p);
+
+       return 0;
+}
index 48c955541e2000239f861bdf7dd5043fa741674c..96a3f86d8e6991e19815008a3a6b8cebe95b1ca8 100644 (file)
@@ -382,6 +382,10 @@ static inline char *git_find_last_dir_sep(const char *path)
 #define find_last_dir_sep git_find_last_dir_sep
 #endif
 
+#ifndef query_user_email
+#define query_user_email() NULL
+#endif
+
 #if defined(__HP_cc) && (__HP_cc >= 61000)
 #define NORETURN __attribute__((noreturn))
 #define NORETURN_PTR
@@ -408,6 +412,8 @@ static inline char *git_find_last_dir_sep(const char *path)
 #define LAST_ARG_MUST_BE_NULL
 #endif
 
+#define MAYBE_UNUSED __attribute__((__unused__))
+
 #include "compat/bswap.h"
 
 #include "wildmatch.h"
similarity index 89%
rename from git-rebase.sh
rename to git-legacy-rebase.sh
index 45b6ee9c0ec14e924a5864fa31d93f0ca65b32d0..75a08b2683e76e6832475bc91e4d4f38b3d3bc3c 100755 (executable)
@@ -57,12 +57,7 @@ cd_to_toplevel
 LF='
 '
 ok_to_skip_pre_rebase=
-resolvemsg="
-$(gettext 'Resolve all conflicts manually, mark them as resolved with
-"git add/rm <conflicted_files>", then run "git rebase --continue".
-You can instead skip this commit: run "git rebase --skip".
-To abort and get back to the state before "git rebase", run "git rebase --abort".')
-"
+
 squash_onto=
 unset onto
 unset restrict_revision
@@ -102,6 +97,7 @@ case "$(git config --bool commit.gpgsign)" in
 true)  gpg_sign_opt=-S ;;
 *)     gpg_sign_opt= ;;
 esac
+. git-rebase--common
 
 read_basic_state () {
        test -f "$state_dir/head-name" &&
@@ -132,67 +128,6 @@ read_basic_state () {
        }
 }
 
-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 = "$verbose" && : > "$state_dir"/verbose
-       test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
-       test -n "$strategy_opts" && echo "$strategy_opts" > \
-               "$state_dir"/strategy_opts
-       test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
-               "$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
-}
-
-output () {
-       case "$verbose" in
-       '')
-               output=$("$@" 2>&1 )
-               status=$?
-               test $status != 0 && printf "%s\n" "$output"
-               return $status
-               ;;
-       *)
-               "$@"
-               ;;
-       esac
-}
-
-move_to_original_branch () {
-       case "$head_name" in
-       refs/*)
-               message="rebase finished: $head_name onto $onto"
-               git update-ref -m "$message" \
-                       $head_name $(git rev-parse HEAD) $orig_head &&
-               git symbolic-ref \
-                       -m "rebase finished: returning to $head_name" \
-                       HEAD $head_name ||
-               die "$(eval_gettext "Could not move back to \$head_name")"
-               ;;
-       esac
-}
-
-apply_autostash () {
-       if test -f "$state_dir/autostash"
-       then
-               stash_sha1=$(cat "$state_dir/autostash")
-               if git stash apply $stash_sha1 >/dev/null 2>&1
-               then
-                       echo "$(gettext 'Applied autostash.')" >&2
-               else
-                       git stash store -m "autostash" -q $stash_sha1 ||
-                       die "$(eval_gettext "Cannot store \$stash_sha1")"
-                       gettext '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.
-' >&2
-               fi
-       fi
-}
-
 finish_rebase () {
        rm -f "$(git rev-parse --git-path REBASE_HEAD)"
        apply_autostash &&
@@ -200,26 +135,63 @@ finish_rebase () {
        rm -rf "$state_dir"
 }
 
+run_interactive () {
+       GIT_CHERRY_PICK_HELP="$resolvemsg"
+       export GIT_CHERRY_PICK_HELP
+
+       test -n "$keep_empty" && keep_empty="--keep-empty"
+       test -n "$rebase_merges" && rebase_merges="--rebase-merges"
+       test -n "$rebase_cousins" && rebase_cousins="--rebase-cousins"
+       test -n "$autosquash" && autosquash="--autosquash"
+       test -n "$verbose" && verbose="--verbose"
+       test -n "$force_rebase" && force_rebase="--no-ff"
+       test -n "$restrict_revision" && \
+               restrict_revision="--restrict-revision=^$restrict_revision"
+       test -n "$upstream" && upstream="--upstream=$upstream"
+       test -n "$onto" && onto="--onto=$onto"
+       test -n "$squash_onto" && squash_onto="--squash-onto=$squash_onto"
+       test -n "$onto_name" && onto_name="--onto-name=$onto_name"
+       test -n "$head_name" && head_name="--head-name=$head_name"
+       test -n "$strategy" && strategy="--strategy=$strategy"
+       test -n "$strategy_opts" && strategy_opts="--strategy-opts=$strategy_opts"
+       test -n "$switch_to" && switch_to="--switch-to=$switch_to"
+       test -n "$cmd" && cmd="--cmd=$cmd"
+       test -n "$action" && action="--$action"
+
+       exec git rebase--interactive "$action" "$keep_empty" "$rebase_merges" "$rebase_cousins" \
+               "$upstream" "$onto" "$squash_onto" "$restrict_revision" \
+               "$allow_empty_message" "$autosquash" "$verbose" \
+               "$force_rebase" "$onto_name" "$head_name" "$strategy" \
+               "$strategy_opts" "$cmd" "$switch_to" \
+               "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff"
+}
+
 run_specific_rebase () {
        if [ "$interactive_rebase" = implied ]; then
                GIT_EDITOR=:
                export GIT_EDITOR
                autosquash=
        fi
-       . git-rebase--$type
 
-       if test -z "$preserve_merges"
+       if test -n "$interactive_rebase" -a -z "$preserve_merges"
        then
-               git_rebase__$type
+               run_interactive
        else
-               git_rebase__preserve_merges
+               . git-rebase--$type
+
+               if test -z "$preserve_merges"
+               then
+                       git_rebase__$type
+               else
+                       git_rebase__preserve_merges
+               fi
        fi
 
        ret=$?
        if test $ret -eq 0
        then
                finish_rebase
-       elif test $ret -eq 2 # special exit status for rebase -i
+       elif test $ret -eq 2 # special exit status for rebase -p
        then
                apply_autostash &&
                rm -rf "$state_dir" &&
index 9a8b97a2aba9aa9fabb095558c03cb8f27df3d50..83bf52494cd26437eaa3c755c3359951937f1ab2 100644 (file)
@@ -350,17 +350,23 @@ guess_merge_tool () {
 }
 
 get_configured_merge_tool () {
-       # Diff mode first tries diff.tool and falls back to merge.tool.
-       # Merge mode only checks merge.tool
+       # If first argument is true, find the guitool instead
+       if test "$1" = true
+       then
+               gui_prefix=gui
+       fi
+
+       # Diff mode first tries diff.(gui)tool and falls back to merge.(gui)tool.
+       # Merge mode only checks merge.(gui)tool
        if diff_mode
        then
-               merge_tool=$(git config diff.tool || git config merge.tool)
+               merge_tool=$(git config diff.${gui_prefix}tool || git config merge.${gui_prefix}tool)
        else
-               merge_tool=$(git config merge.tool)
+               merge_tool=$(git config merge.${gui_prefix}tool)
        fi
        if test -n "$merge_tool" && ! valid_tool "$merge_tool"
        then
-               echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
+               echo >&2 "git config option $TOOL_MODE.${gui_prefix}tool set to unknown tool: $merge_tool"
                echo >&2 "Resetting to default..."
                return 1
        fi
index d07c7f387cf478bbd93d1dada1afa1de80db0fbf..01b9ad59b24ff77221d5db4118c9d0265dc25fad 100755 (executable)
@@ -9,7 +9,7 @@
 # at the discretion of Junio C Hamano.
 #
 
-USAGE='[--tool=tool] [--tool-help] [-y|--no-prompt|--prompt] [-O<orderfile>] [file to merge] ...'
+USAGE='[--tool=tool] [--tool-help] [-y|--no-prompt|--prompt] [-g|--gui|--no-gui] [-O<orderfile>] [file to merge] ...'
 SUBDIRECTORY_OK=Yes
 NONGIT_OK=Yes
 OPTIONS_SPEC=
@@ -389,6 +389,7 @@ print_noop_and_exit () {
 
 main () {
        prompt=$(git config --bool mergetool.prompt)
+       gui_tool=false
        guessed_merge_tool=false
        orderfile=
 
@@ -414,6 +415,12 @@ main () {
                                shift ;;
                        esac
                        ;;
+               --no-gui)
+                       gui_tool=false
+                       ;;
+               -g|--gui)
+                       gui_tool=true
+                       ;;
                -y|--no-prompt)
                        prompt=false
                        ;;
@@ -443,7 +450,7 @@ main () {
        if test -z "$merge_tool"
        then
                # Check if a merge tool has been configured
-               merge_tool=$(get_configured_merge_tool)
+               merge_tool=$(get_configured_merge_tool $gui_tool)
                # Try to guess an appropriate merge tool if no tool has been set.
                if test -z "$merge_tool"
                then
index 7fab255584e5bca70aef305f6ead73330694a88b..1998c3e141bec5142032f08623b916d7741e64ac 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -1306,6 +1306,9 @@ class GitLFS(LargeFileSystem):
             return LargeFileSystem.processContent(self, git_mode, relPath, contents)
 
 class Command:
+    delete_actions = ( "delete", "move/delete", "purge" )
+    add_actions = ( "add", "move/add" )
+
     def __init__(self):
         self.usage = "usage: %prog [options]"
         self.needsGit = True
@@ -2524,7 +2527,6 @@ class View(object):
         return ""
 
 class P4Sync(Command, P4UserMap):
-    delete_actions = ( "delete", "move/delete", "purge" )
 
     def __init__(self):
         Command.__init__(self)
@@ -2612,20 +2614,7 @@ class P4Sync(Command, P4UserMap):
         if self.verbose:
             print("checkpoint finished: " + out)
 
-    def cmp_shelved(self, path, filerev, revision):
-        """ Determine if a path at revision #filerev is the same as the file
-            at revision @revision for a shelved changelist. If they don't match,
-            unshelving won't be safe (we will get other changes mixed in).
-
-            This is comparing the revision that the shelved changelist is *based* on, not
-            the shelved changelist itself.
-        """
-        ret = p4Cmd(["diff2", "{0}#{1}".format(path, filerev), "{0}@{1}".format(path, revision)])
-        if verbose:
-            print("p4 diff2 path %s filerev %s revision %s => %s" % (path, filerev, revision, ret))
-        return ret["status"] == "identical"
-
-    def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0, origin_revision = 0):
+    def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0):
         self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
                              for path in self.cloneExclude]
         files = []
@@ -2650,17 +2639,6 @@ class P4Sync(Command, P4UserMap):
             file["type"] = commit["type%s" % fnum]
             if shelved:
                 file["shelved_cl"] = int(shelved_cl)
-
-                # For shelved changelists, check that the revision of each file that the
-                # shelve was based on matches the revision that we are using for the
-                # starting point for git-fast-import (self.initialParent). Otherwise
-                # the resulting diff will contain deltas from multiple commits.
-
-                if file["action"] != "add" and \
-                    not self.cmp_shelved(path, file["rev"], origin_revision):
-                    sys.exit("change {0} not based on {1} for {2}, cannot unshelve".format(
-                        commit["change"], self.initialParent, path))
-
             files.append(file)
             fnum = fnum + 1
         return files
@@ -2775,7 +2753,10 @@ class P4Sync(Command, P4UserMap):
         relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
         relPath = self.encodeWithUTF8(relPath)
         if verbose:
-            size = int(self.stream_file['fileSize'])
+            if 'fileSize' in self.stream_file:
+                size = int(self.stream_file['fileSize'])
+            else:
+                size = 0 # deleted files don't get a fileSize apparently
             sys.stdout.write('\r%s --> %s (%i MB)\n' % (file['depotFile'], relPath, size/1024/1024))
             sys.stdout.flush()
 
@@ -3029,7 +3010,7 @@ class P4Sync(Command, P4UserMap):
             print('Ignoring file outside of prefix: {0}'.format(path))
         return hasPrefix
 
-    def commit(self, details, files, branch, parent = ""):
+    def commit(self, details, files, branch, parent = "", allow_empty=False):
         epoch = details["time"]
         author = details["user"]
         jobs = self.extractJobsFromCommit(details)
@@ -3043,7 +3024,10 @@ class P4Sync(Command, P4UserMap):
         files = [f for f in files
             if self.inClientSpec(f['path']) and self.hasBranchPrefix(f['path'])]
 
-        if not files and not gitConfigBool('git-p4.keepEmptyCommits'):
+        if gitConfigBool('git-p4.keepEmptyCommits'):
+            allow_empty = True
+
+        if not files and not allow_empty:
             print('Ignoring revision {0} as it would produce an empty commit.'
                 .format(details['change']))
             return
@@ -3384,10 +3368,10 @@ class P4Sync(Command, P4UserMap):
         else:
             return None
 
-    def importChanges(self, changes, shelved=False, origin_revision=0):
+    def importChanges(self, changes, origin_revision=0):
         cnt = 1
         for change in changes:
-            description = p4_describe(change, shelved)
+            description = p4_describe(change)
             self.updateOptionDict(description)
 
             if not self.silent:
@@ -3457,7 +3441,7 @@ class P4Sync(Command, P4UserMap):
                                 print("Parent of %s not found. Committing into head of %s" % (branch, parent))
                             self.commit(description, filesForCommit, branch, parent)
                 else:
-                    files = self.extractFilesFromCommit(description, shelved, change, origin_revision)
+                    files = self.extractFilesFromCommit(description)
                     self.commit(description, files, self.branch,
                                 self.initialParent)
                     # only needed once, to connect to the previous commit
@@ -3953,7 +3937,7 @@ class P4Unshelve(Command):
         ]
         self.verbose = False
         self.noCommit = False
-        self.destbranch = "refs/remotes/p4/unshelved"
+        self.destbranch = "refs/remotes/p4-unshelved"
 
     def renameBranch(self, branch_name):
         """ Rename the existing branch to branch_name.N
@@ -3985,6 +3969,32 @@ class P4Unshelve(Command):
 
         sys.exit("could not find git-p4 commits in {0}".format(self.origin))
 
+    def createShelveParent(self, change, branch_name, sync, origin):
+        """ Create a commit matching the parent of the shelved changelist 'change'
+        """
+        parent_description = p4_describe(change, shelved=True)
+        parent_description['desc'] = 'parent for shelved changelist {}\n'.format(change)
+        files = sync.extractFilesFromCommit(parent_description, shelved=False, shelved_cl=change)
+
+        parent_files = []
+        for f in files:
+            # if it was added in the shelved changelist, it won't exist in the parent
+            if f['action'] in self.add_actions:
+                continue
+
+            # if it was deleted in the shelved changelist it must not be deleted
+            # in the parent - we might even need to create it if the origin branch
+            # does not have it
+            if f['action'] in self.delete_actions:
+                f['action'] = 'add'
+
+            parent_files.append(f)
+
+        sync.commit(parent_description, parent_files, branch_name,
+                parent=origin, allow_empty=True)
+        print("created parent commit for {0} based on {1} in {2}".format(
+            change, self.origin, branch_name))
+
     def run(self, args):
         if len(args) != 1:
             return False
@@ -3994,9 +4004,8 @@ class P4Unshelve(Command):
 
         sync = P4Sync()
         changes = args
-        sync.initialParent = self.origin
 
-        # use the first change in the list to construct the branch to unshelve into
+        # only one change at a time
         change = changes[0]
 
         # if the target branch already exists, rename it
@@ -4009,14 +4018,21 @@ class P4Unshelve(Command):
         sync.suppress_meta_comment = True
 
         settings = self.findLastP4Revision(self.origin)
-        origin_revision = settings['change']
         sync.depotPaths = settings['depot-paths']
         sync.branchPrefixes = sync.depotPaths
 
         sync.openStreams()
         sync.loadUserMapFromCache()
         sync.silent = True
-        sync.importChanges(changes, shelved=True, origin_revision=origin_revision)
+
+        # create a commit for the parent of the shelved changelist
+        self.createShelveParent(change, branch_name, sync, self.origin)
+
+        # create the commit for the shelved changelist itself
+        description = p4_describe(change, True)
+        files = sync.extractFilesFromCommit(description, True, change)
+
+        sync.commit(description, files, branch_name, "")
         sync.closeStreams()
 
         print("unshelved changelist {0} into {1}".format(change, branch_name))
diff --git a/git-rebase--common.sh b/git-rebase--common.sh
new file mode 100644 (file)
index 0000000..7e39d22
--- /dev/null
@@ -0,0 +1,68 @@
+
+resolvemsg="
+$(gettext 'Resolve all conflicts manually, mark them as resolved with
+"git add/rm <conflicted_files>", then run "git rebase --continue".
+You can instead skip this commit: run "git rebase --skip".
+To abort and get back to the state before "git rebase", run "git rebase --abort".')
+"
+
+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 = "$verbose" && : > "$state_dir"/verbose
+       test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
+       test -n "$strategy_opts" && echo "$strategy_opts" > \
+               "$state_dir"/strategy_opts
+       test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
+               "$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
+}
+
+apply_autostash () {
+       if test -f "$state_dir/autostash"
+       then
+               stash_sha1=$(cat "$state_dir/autostash")
+               if git stash apply $stash_sha1 >/dev/null 2>&1
+               then
+                       echo "$(gettext 'Applied autostash.')" >&2
+               else
+                       git stash store -m "autostash" -q $stash_sha1 ||
+                       die "$(eval_gettext "Cannot store \$stash_sha1")"
+                       gettext '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.
+' >&2
+               fi
+       fi
+}
+
+move_to_original_branch () {
+       case "$head_name" in
+       refs/*)
+               message="rebase finished: $head_name onto $onto"
+               git update-ref -m "$message" \
+                       $head_name $(git rev-parse HEAD) $orig_head &&
+               git symbolic-ref \
+                       -m "rebase finished: returning to $head_name" \
+                       HEAD $head_name ||
+               die "$(eval_gettext "Could not move back to \$head_name")"
+               ;;
+       esac
+}
+
+output () {
+       case "$verbose" in
+       '')
+               output=$("$@" 2>&1 )
+               status=$?
+               test $status != 0 && printf "%s\n" "$output"
+               return $status
+               ;;
+       *)
+               "$@"
+               ;;
+       esac
+}
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
deleted file mode 100644 (file)
index 299ded2..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-# This shell script fragment is sourced by git-rebase to implement
-# its interactive mode.  "git rebase --interactive" makes it easy
-# to fix up commits in the middle of a series and rearrange commits.
-#
-# Copyright (c) 2006 Johannes E. Schindelin
-#
-# The original idea comes from Eric W. Biederman, in
-# https://public-inbox.org/git/m1odwkyuf5.fsf_-_@ebiederm.dsl.xmission.com/
-#
-# The file containing rebase commands, comments, and empty lines.
-# This file is created by "git rebase -i" then edited by the user.  As
-# the lines are processed, they are removed from the front of this
-# file and written to the tail of $done.
-todo="$state_dir"/git-rebase-todo
-
-GIT_CHERRY_PICK_HELP="$resolvemsg"
-export GIT_CHERRY_PICK_HELP
-
-comment_char=$(git config --get core.commentchar 2>/dev/null)
-case "$comment_char" in
-'' | auto)
-       comment_char="#"
-       ;;
-?)
-       ;;
-*)
-       comment_char=$(echo "$comment_char" | cut -c1)
-       ;;
-esac
-
-orig_reflog_action="$GIT_REFLOG_ACTION"
-
-comment_for_reflog () {
-       case "$orig_reflog_action" in
-       ''|rebase*)
-               GIT_REFLOG_ACTION="rebase -i ($1)"
-               export GIT_REFLOG_ACTION
-               ;;
-       esac
-}
-
-append_todo_help () {
-       gettext "
-Commands:
-p, pick <commit> = use commit
-r, reword <commit> = use commit, but edit the commit message
-e, edit <commit> = use commit, but stop for amending
-s, squash <commit> = use commit, but meld into previous commit
-f, fixup <commit> = like \"squash\", but discard this commit's log message
-x, exec <command> = run command (the rest of the line) using shell
-d, drop <commit> = remove commit
-l, label <label> = label current HEAD with a name
-t, reset <label> = reset HEAD to a label
-m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
-.       create a merge commit using the original merge commit's
-.       message (or the oneline, if no original merge commit was
-.       specified). Use -c <commit> to reword the commit message.
-
-These lines can be re-ordered; they are executed from top to bottom.
-" | git stripspace --comment-lines >>"$todo"
-
-       if test $(get_missing_commit_check_level) = error
-       then
-               gettext "
-Do not remove any line. Use 'drop' explicitly to remove a commit.
-" | git stripspace --comment-lines >>"$todo"
-       else
-               gettext "
-If you remove a line here THAT COMMIT WILL BE LOST.
-" | git stripspace --comment-lines >>"$todo"
-       fi
-}
-
-die_abort () {
-       apply_autostash
-       rm -rf "$state_dir"
-       die "$1"
-}
-
-has_action () {
-       test -n "$(git stripspace --strip-comments <"$1")"
-}
-
-git_sequence_editor () {
-       if test -z "$GIT_SEQUENCE_EDITOR"
-       then
-               GIT_SEQUENCE_EDITOR="$(git config sequence.editor)"
-               if [ -z "$GIT_SEQUENCE_EDITOR" ]
-               then
-                       GIT_SEQUENCE_EDITOR="$(git var GIT_EDITOR)" || return $?
-               fi
-       fi
-
-       eval "$GIT_SEQUENCE_EDITOR" '"$@"'
-}
-
-expand_todo_ids() {
-       git rebase--helper --expand-ids
-}
-
-collapse_todo_ids() {
-       git rebase--helper --shorten-ids
-}
-
-# Switch to the branch in $into and notify it in the reflog
-checkout_onto () {
-       GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
-       output git checkout $onto || die_abort "$(gettext "could not detach HEAD")"
-       git update-ref ORIG_HEAD $orig_head
-}
-
-get_missing_commit_check_level () {
-       check_level=$(git config --get rebase.missingCommitsCheck)
-       check_level=${check_level:-ignore}
-       # Don't be case sensitive
-       printf '%s' "$check_level" | tr 'A-Z' 'a-z'
-}
-
-# Initiate an action. If the cannot be any
-# further action it  may exec a command
-# or exit and not return.
-#
-# TODO: Consider a cleaner return model so it
-# never exits and always return 0 if process
-# is complete.
-#
-# Parameter 1 is the action to initiate.
-#
-# Returns 0 if the action was able to complete
-# and if 1 if further processing is required.
-initiate_action () {
-       case "$1" in
-       continue)
-               exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
-                    --continue
-               ;;
-       skip)
-               git rerere clear
-               exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
-                    --continue
-               ;;
-       edit-todo)
-               git stripspace --strip-comments <"$todo" >"$todo".new
-               mv -f "$todo".new "$todo"
-               collapse_todo_ids
-               append_todo_help
-               gettext "
-You are editing the todo file of an ongoing interactive rebase.
-To continue rebase after editing, run:
-    git rebase --continue
-
-" | git stripspace --comment-lines >>"$todo"
-
-               git_sequence_editor "$todo" ||
-                       die "$(gettext "Could not execute editor")"
-               expand_todo_ids
-
-               exit
-               ;;
-       show-current-patch)
-               exec git show REBASE_HEAD --
-               ;;
-       *)
-               return 1 # continue
-               ;;
-       esac
-}
-
-setup_reflog_action () {
-       comment_for_reflog start
-
-       if test ! -z "$switch_to"
-       then
-               GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
-               output git checkout "$switch_to" -- ||
-                       die "$(eval_gettext "Could not checkout \$switch_to")"
-
-               comment_for_reflog start
-       fi
-}
-
-init_basic_state () {
-       orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
-       mkdir -p "$state_dir" || die "$(eval_gettext "Could not create temporary \$state_dir")"
-       rm -f "$(git rev-parse --git-path REBASE_HEAD)"
-
-       : > "$state_dir"/interactive || die "$(gettext "Could not mark as interactive")"
-       write_basic_state
-}
-
-init_revisions_and_shortrevisions () {
-       shorthead=$(git rev-parse --short $orig_head)
-       shortonto=$(git rev-parse --short $onto)
-       if test -z "$rebase_root"
-               # this is now equivalent to ! -z "$upstream"
-       then
-               shortupstream=$(git rev-parse --short $upstream)
-               revisions=$upstream...$orig_head
-               shortrevisions=$shortupstream..$shorthead
-       else
-               revisions=$onto...$orig_head
-               shortrevisions=$shorthead
-               test -z "$squash_onto" ||
-               echo "$squash_onto" >"$state_dir"/squash-onto
-       fi
-}
-
-complete_action() {
-       test -s "$todo" || echo noop >> "$todo"
-       test -z "$autosquash" || git rebase--helper --rearrange-squash || exit
-       test -n "$cmd" && git rebase--helper --add-exec-commands "$cmd"
-
-       todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
-       todocount=${todocount##* }
-
-cat >>"$todo" <<EOF
-
-$comment_char $(eval_ngettext \
-       "Rebase \$shortrevisions onto \$shortonto (\$todocount command)" \
-       "Rebase \$shortrevisions onto \$shortonto (\$todocount commands)" \
-       "$todocount")
-EOF
-       append_todo_help
-       gettext "
-       However, if you remove everything, the rebase will be aborted.
-
-       " | git stripspace --comment-lines >>"$todo"
-
-       if test -z "$keep_empty"
-       then
-               printf '%s\n' "$comment_char $(gettext "Note that empty commits are commented out")" >>"$todo"
-       fi
-
-
-       has_action "$todo" ||
-               return 2
-
-       cp "$todo" "$todo".backup
-       collapse_todo_ids
-       git_sequence_editor "$todo" ||
-               die_abort "$(gettext "Could not execute editor")"
-
-       has_action "$todo" ||
-               return 2
-
-       git rebase--helper --check-todo-list || {
-               ret=$?
-               checkout_onto
-               exit $ret
-       }
-
-       expand_todo_ids
-
-       test -n "$force_rebase" ||
-       onto="$(git rebase--helper --skip-unnecessary-picks)" ||
-       die "Could not skip unnecessary pick commands"
-
-       checkout_onto
-       require_clean_work_tree "rebase"
-       exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
-            --continue
-}
-
-git_rebase__interactive () {
-       initiate_action "$action"
-       ret=$?
-       if test $ret = 0; then
-               return 0
-       fi
-
-       setup_reflog_action
-       init_basic_state
-
-       init_revisions_and_shortrevisions
-
-       git rebase--helper --make-script ${keep_empty:+--keep-empty} \
-               ${rebase_merges:+--rebase-merges} \
-               ${rebase_cousins:+--rebase-cousins} \
-               $revisions ${restrict_revision+^$restrict_revision} >"$todo" ||
-       die "$(gettext "Could not generate todo list")"
-
-       complete_action
-}
index c214c5e4d6ce21996f16031ec14dd624b75a9939..afbb65765d46102339068e7e9aa397fcf88ee6a5 100644 (file)
@@ -711,11 +711,11 @@ do_rest () {
 }
 
 expand_todo_ids() {
-       git rebase--helper --expand-ids
+       git rebase--interactive --expand-ids
 }
 
 collapse_todo_ids() {
-       git rebase--helper --shorten-ids
+       git rebase--interactive --shorten-ids
 }
 
 # Switch to the branch in $into and notify it in the reflog
@@ -876,8 +876,8 @@ init_revisions_and_shortrevisions () {
 
 complete_action() {
        test -s "$todo" || echo noop >> "$todo"
-       test -z "$autosquash" || git rebase--helper --rearrange-squash || exit
-       test -n "$cmd" && git rebase--helper --add-exec-commands "$cmd"
+       test -z "$autosquash" || git rebase--interactive --rearrange-squash || exit
+       test -n "$cmd" && git rebase--interactive --add-exec-commands --cmd "$cmd"
 
        todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
        todocount=${todocount##* }
@@ -912,7 +912,7 @@ However, if you remove everything, the rebase will be aborted.
        has_action "$todo" ||
                return 2
 
-       git rebase--helper --check-todo-list || {
+       git rebase--interactive --check-todo-list || {
                ret=$?
                checkout_onto
                exit $ret
index 2be5dac337a0e2f4a0ea1538c7873aa43382cb04..58c6aa9d0eca3e692e5c8c5df538afdb8f8fe63d 100755 (executable)
@@ -94,7 +94,7 @@ git send-email --dump-aliases
     --identity              <str>  * Use the sendemail.<id> options.
     --to-cmd                <str>  * Email To: via `<str> \$patch_path`
     --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
-    --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, all.
+    --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, misc-by, all.
     --[no-]cc-cover                * Email Cc: addresses in the cover letter.
     --[no-]to-cover                * Email To: addresses in the cover letter.
     --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on.
@@ -454,13 +454,13 @@ my(%suppress_cc);
 if (@suppress_cc) {
        foreach my $entry (@suppress_cc) {
                die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
-                       unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
+                       unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc|misc-by)$/;
                $suppress_cc{$entry} = 1;
        }
 }
 
 if ($suppress_cc{'all'}) {
-       foreach my $entry (qw (cccmd cc author self sob body bodycc)) {
+       foreach my $entry (qw (cccmd cc author self sob body bodycc misc-by)) {
                $suppress_cc{$entry} = 1;
        }
        delete $suppress_cc{'all'};
@@ -471,7 +471,7 @@ $suppress_cc{'self'} = $suppress_from if defined $suppress_from;
 $suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;
 
 if ($suppress_cc{'body'}) {
-       foreach my $entry (qw (sob bodycc)) {
+       foreach my $entry (qw (sob bodycc misc-by)) {
                $suppress_cc{$entry} = 1;
        }
        delete $suppress_cc{'body'};
@@ -1681,7 +1681,7 @@ sub process_file {
        # Now parse the message body
        while(<$fh>) {
                $message .=  $_;
-               if (/^(Signed-off-by|Cc): (.*)/i) {
+               if (/^([a-z-]*-by|Cc): (.*)/i) {
                        chomp;
                        my ($what, $c) = ($1, $2);
                        # strip garbage for the address we'll use:
@@ -1691,8 +1691,18 @@ sub process_file {
                        if ($sc eq $sender) {
                                next if ($suppress_cc{'self'});
                        } else {
-                               next if $suppress_cc{'sob'} and $what =~ /Signed-off-by/i;
-                               next if $suppress_cc{'bodycc'} and $what =~ /Cc/i;
+                               if ($what =~ /^Signed-off-by$/i) {
+                                       next if $suppress_cc{'sob'};
+                               } elsif ($what =~ /-by$/i) {
+                                       next if $suppress_cc{'misc-by'};
+                               } elsif ($what =~ /Cc/i) {
+                                       next if $suppress_cc{'bodycc'};
+                               }
+                       }
+                       if ($c !~ /.+@.+|<.+>/) {
+                               printf("(body) Ignoring %s from line '%s'\n",
+                                       $what, $_) unless $quiet;
+                               next;
                        }
                        push @cc, $c;
                        printf(__("(body) Adding cc: %s from line '%s'\n"),
index 1b568e29b9a09af9fdecf19ddb75ffd2bdc6e094..c09eb3e03d25510b2768d8f9c144cab2597fc000 100755 (executable)
@@ -82,6 +82,11 @@ isnumber()
        n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
 }
 
+# Given a full hex object ID, is this the zero OID?
+is_zero_oid () {
+       echo "$1" | sane_egrep '^0+$' >/dev/null 2>&1
+}
+
 # Sanitize the local git environment for use within a submodule. We
 # can't simply use clear_local_git_env since we want to preserve some
 # of the settings from GIT_CONFIG_PARAMETERS.
@@ -780,7 +785,7 @@ cmd_summary() {
        while read -r mod_src mod_dst sha1_src sha1_dst status name
        do
                if test -z "$cached" &&
-                       test $sha1_dst = 0000000000000000000000000000000000000000
+                       is_zero_oid $sha1_dst
                then
                        case "$mod_dst" in
                        160000)
diff --git a/git.c b/git.c
index 5920f8019bb3b26db0c7c50f9ea02f8ff96230e8..2f604a41eaf620f4d185c612cd7c6aee975faa67 100644 (file)
--- a/git.c
+++ b/git.c
@@ -318,6 +318,9 @@ static int handle_alias(int *argcp, const char ***argv)
        alias_command = (*argv)[0];
        alias_string = alias_lookup(alias_command);
        if (alias_string) {
+               if (*argcp > 1 && !strcmp((*argv)[1], "-h"))
+                       fprintf_ln(stderr, _("'%s' is aliased to '%s'"),
+                                  alias_command, alias_string);
                if (alias_string[0] == '!') {
                        struct child_process child = CHILD_PROCESS_INIT;
                        int nongit_ok;
@@ -523,7 +526,13 @@ static struct cmd_struct commands[] = {
        { "push", cmd_push, RUN_SETUP },
        { "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
        { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
-       { "rebase--helper", cmd_rebase__helper, RUN_SETUP | NEED_WORK_TREE },
+       /*
+        * NEEDSWORK: Until the rebase is independent and needs no redirection
+        * to rebase shell script this is kept as is, then should be changed to
+        * RUN_SETUP | NEED_WORK_TREE
+        */
+       { "rebase", cmd_rebase },
+       { "rebase--interactive", cmd_rebase__interactive, RUN_SETUP | NEED_WORK_TREE },
        { "receive-pack", cmd_receive_pack },
        { "reflog", cmd_reflog, RUN_SETUP },
        { "remote", cmd_remote, RUN_SETUP },
index db17d65f8ac977ddc5204130cb0af6a7565ae04b..8ed274533f87198a74a3660809c25932fa43124a 100644 (file)
@@ -73,50 +73,115 @@ void signature_check_clear(struct signature_check *sigc)
        FREE_AND_NULL(sigc->gpg_status);
        FREE_AND_NULL(sigc->signer);
        FREE_AND_NULL(sigc->key);
+       FREE_AND_NULL(sigc->fingerprint);
+       FREE_AND_NULL(sigc->primary_key_fingerprint);
 }
 
+/* An exclusive status -- only one of them can appear in output */
+#define GPG_STATUS_EXCLUSIVE   (1<<0)
+/* The status includes key identifier */
+#define GPG_STATUS_KEYID       (1<<1)
+/* The status includes user identifier */
+#define GPG_STATUS_UID         (1<<2)
+/* The status includes key fingerprints */
+#define GPG_STATUS_FINGERPRINT (1<<3)
+
+/* Short-hand for standard exclusive *SIG status with keyid & UID */
+#define GPG_STATUS_STDSIG      (GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID|GPG_STATUS_UID)
+
 static struct {
        char result;
        const char *check;
+       unsigned int flags;
 } sigcheck_gpg_status[] = {
-       { 'G', "\n[GNUPG:] GOODSIG " },
-       { 'B', "\n[GNUPG:] BADSIG " },
-       { 'U', "\n[GNUPG:] TRUST_NEVER" },
-       { 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
-       { 'E', "\n[GNUPG:] ERRSIG "},
-       { 'X', "\n[GNUPG:] EXPSIG "},
-       { 'Y', "\n[GNUPG:] EXPKEYSIG "},
-       { 'R', "\n[GNUPG:] REVKEYSIG "},
+       { 'G', "GOODSIG ", GPG_STATUS_STDSIG },
+       { 'B', "BADSIG ", GPG_STATUS_STDSIG },
+       { 'U', "TRUST_NEVER", 0 },
+       { 'U', "TRUST_UNDEFINED", 0 },
+       { 'E', "ERRSIG ", GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID },
+       { 'X', "EXPSIG ", GPG_STATUS_STDSIG },
+       { 'Y', "EXPKEYSIG ", GPG_STATUS_STDSIG },
+       { 'R', "REVKEYSIG ", GPG_STATUS_STDSIG },
+       { 0, "VALIDSIG ", GPG_STATUS_FINGERPRINT },
 };
 
 static void parse_gpg_output(struct signature_check *sigc)
 {
        const char *buf = sigc->gpg_status;
-       int i;
-
-       /* Iterate over all search strings */
-       for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
-               const char *found, *next;
-
-               if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
-                       found = strstr(buf, sigcheck_gpg_status[i].check);
-                       if (!found)
-                               continue;
-                       found += strlen(sigcheck_gpg_status[i].check);
-               }
-               sigc->result = sigcheck_gpg_status[i].result;
-               /* The trust messages are not followed by key/signer information */
-               if (sigc->result != 'U') {
-                       next = strchrnul(found, ' ');
-                       sigc->key = xmemdupz(found, next - found);
-                       /* The ERRSIG message is not followed by signer information */
-                       if (*next && sigc-> result != 'E') {
-                               found = next + 1;
-                               next = strchrnul(found, '\n');
-                               sigc->signer = xmemdupz(found, next - found);
+       const char *line, *next;
+       int i, j;
+       int seen_exclusive_status = 0;
+
+       /* Iterate over all lines */
+       for (line = buf; *line; line = strchrnul(line+1, '\n')) {
+               while (*line == '\n')
+                       line++;
+               /* Skip lines that don't start with GNUPG status */
+               if (!skip_prefix(line, "[GNUPG:] ", &line))
+                       continue;
+
+               /* Iterate over all search strings */
+               for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
+                       if (skip_prefix(line, sigcheck_gpg_status[i].check, &line)) {
+                               if (sigcheck_gpg_status[i].flags & GPG_STATUS_EXCLUSIVE) {
+                                       if (seen_exclusive_status++)
+                                               goto found_duplicate_status;
+                               }
+
+                               if (sigcheck_gpg_status[i].result)
+                                       sigc->result = sigcheck_gpg_status[i].result;
+                               /* Do we have key information? */
+                               if (sigcheck_gpg_status[i].flags & GPG_STATUS_KEYID) {
+                                       next = strchrnul(line, ' ');
+                                       free(sigc->key);
+                                       sigc->key = xmemdupz(line, next - line);
+                                       /* Do we have signer information? */
+                                       if (*next && (sigcheck_gpg_status[i].flags & GPG_STATUS_UID)) {
+                                               line = next + 1;
+                                               next = strchrnul(line, '\n');
+                                               free(sigc->signer);
+                                               sigc->signer = xmemdupz(line, next - line);
+                                       }
+                               }
+                               /* Do we have fingerprint? */
+                               if (sigcheck_gpg_status[i].flags & GPG_STATUS_FINGERPRINT) {
+                                       next = strchrnul(line, ' ');
+                                       free(sigc->fingerprint);
+                                       sigc->fingerprint = xmemdupz(line, next - line);
+
+                                       /* Skip interim fields */
+                                       for (j = 9; j > 0; j--) {
+                                               if (!*next)
+                                                       break;
+                                               line = next + 1;
+                                               next = strchrnul(line, ' ');
+                                       }
+
+                                       next = strchrnul(line, '\n');
+                                       free(sigc->primary_key_fingerprint);
+                                       sigc->primary_key_fingerprint = xmemdupz(line, next - line);
+                               }
+
+                               break;
                        }
                }
        }
+       return;
+
+found_duplicate_status:
+       /*
+        * GOODSIG, BADSIG etc. can occur only once for each signature.
+        * Therefore, if we had more than one then we're dealing with multiple
+        * signatures.  We don't support them currently, and they're rather
+        * hard to create, so something is likely fishy and we should reject
+        * them altogether.
+        */
+       sigc->result = 'E';
+       /* Clear partial data to avoid confusion */
+       FREE_AND_NULL(sigc->primary_key_fingerprint);
+       FREE_AND_NULL(sigc->fingerprint);
+       FREE_AND_NULL(sigc->signer);
+       FREE_AND_NULL(sigc->key);
 }
 
 int check_signature(const char *payload, size_t plen, const char *signature,
index acf50c46109e57bcc7809ab394c73a4d050ad9f0..3e624ec289ab5f46b686f9eb2489c997bca23232 100644 (file)
@@ -23,6 +23,8 @@ struct signature_check {
        char result;
        char *signer;
        char *key;
+       char *fingerprint;
+       char *primary_key_fingerprint;
 };
 
 void signature_check_clear(struct signature_check *sigc);
diff --git a/graph.c b/graph.c
index e1f6d3bddb38aadd4e439aea30f542d286ecb98e..f53135485f565baf7a0de1a5e4cffe2a4b2b55bd 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -842,27 +842,55 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
 }
 
 /*
- * Draw an octopus merge and return the number of characters written.
+ * Draw the horizontal dashes of an octopus merge and return the number of
+ * characters written.
  */
 static int graph_draw_octopus_merge(struct git_graph *graph,
                                    struct strbuf *sb)
 {
        /*
-        * Here dashless_commits represents the number of parents
-        * which don't need to have dashes (because their edges fit
-        * neatly under the commit).
-        */
-       const int dashless_commits = 2;
-       int col_num, i;
-       int num_dashes =
-               ((graph->num_parents - dashless_commits) * 2) - 1;
-       for (i = 0; i < num_dashes; i++) {
-               col_num = (i / 2) + dashless_commits + graph->commit_index;
-               strbuf_write_column(sb, &graph->new_columns[col_num], '-');
+        * Here dashless_parents represents the number of parents which don't
+        * need to have dashes (the edges labeled "0" and "1").  And
+        * dashful_parents are the remaining ones.
+        *
+        * | *---.
+        * | |\ \ \
+        * | | | | |
+        * x 0 1 2 3
+        *
+        */
+       const int dashless_parents = 2;
+       int dashful_parents = graph->num_parents - dashless_parents;
+
+       /*
+        * Usually, we add one new column for each parent (like the diagram
+        * above) but sometimes the first parent goes into an existing column,
+        * like this:
+        *
+        * | *---.
+        * | |\ \ \
+        * |/ / / /
+        * x 0 1 2
+        *
+        * In which case the number of parents will be one greater than the
+        * number of added columns.
+        */
+       int added_cols = (graph->num_new_columns - graph->num_columns);
+       int parent_in_old_cols = graph->num_parents - added_cols;
+
+       /*
+        * In both cases, commit_index corresponds to the edge labeled "0".
+        */
+       int first_col = graph->commit_index + dashless_parents
+           - parent_in_old_cols;
+
+       int i;
+       for (i = 0; i < dashful_parents; i++) {
+               strbuf_write_column(sb, &graph->new_columns[i+first_col], '-');
+               strbuf_write_column(sb, &graph->new_columns[i+first_col],
+                                   i == dashful_parents-1 ? '.' : '-');
        }
-       col_num = (i / 2) + dashless_commits + graph->commit_index;
-       strbuf_write_column(sb, &graph->new_columns[col_num], '.');
-       return num_dashes + 1;
+       return 2 * dashful_parents;
 }
 
 static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
diff --git a/grep.c b/grep.c
index 2b26cee08d559ceba6dd5a83ae90aaa0155ebca8..f6bd89e40b7f7a8442a6e0a302a1e6080df5afbd 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -11,7 +11,8 @@
 #include "help.h"
 
 static int grep_source_load(struct grep_source *gs);
-static int grep_source_is_binary(struct grep_source *gs);
+static int grep_source_is_binary(struct grep_source *gs,
+                                struct index_state *istate);
 
 static struct grep_opt grep_defaults;
 
@@ -42,7 +43,7 @@ static void color_set(char *dst, const char *color_bytes)
  * We could let the compiler do this, but without C99 initializers
  * the code gets unwieldy and unreadable, so...
  */
-void init_grep_defaults(void)
+void init_grep_defaults(struct repository *repo)
 {
        struct grep_opt *opt = &grep_defaults;
        static int run_once;
@@ -52,6 +53,7 @@ void init_grep_defaults(void)
        run_once++;
 
        memset(opt, 0, sizeof(*opt));
+       opt->repo = repo;
        opt->relative = 1;
        opt->pathname = 1;
        opt->max_depth = -1;
@@ -149,12 +151,13 @@ int grep_config(const char *var, const char *value, void *cb)
  * default values from the template we read the configuration
  * information in an earlier call to git_config(grep_config).
  */
-void grep_init(struct grep_opt *opt, const char *prefix)
+void grep_init(struct grep_opt *opt, struct repository *repo, const char *prefix)
 {
        struct grep_opt *def = &grep_defaults;
        int i;
 
        memset(opt, 0, sizeof(*opt));
+       opt->repo = repo;
        opt->prefix = prefix;
        opt->prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
        opt->pattern_tail = &opt->pattern_list;
@@ -1545,7 +1548,7 @@ static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bo
 {
        xdemitconf_t *xecfg = opt->priv;
        if (xecfg && !xecfg->find_func) {
-               grep_source_load_driver(gs);
+               grep_source_load_driver(gs, opt->repo->index);
                if (gs->driver->funcname.pattern) {
                        const struct userdiff_funcname *pe = &gs->driver->funcname;
                        xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
@@ -1708,7 +1711,8 @@ static int look_ahead(struct grep_opt *opt,
        return 0;
 }
 
-static int fill_textconv_grep(struct userdiff_driver *driver,
+static int fill_textconv_grep(struct repository *r,
+                             struct userdiff_driver *driver,
                              struct grep_source *gs)
 {
        struct diff_filespec *df;
@@ -1741,7 +1745,7 @@ static int fill_textconv_grep(struct userdiff_driver *driver,
         * structure.
         */
        grep_read_lock();
-       size = fill_textconv(driver, df, &buf);
+       size = fill_textconv(r, driver, df, &buf);
        grep_read_unlock();
        free_filespec(df);
 
@@ -1801,7 +1805,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
        opt->last_shown = 0;
 
        if (opt->allow_textconv) {
-               grep_source_load_driver(gs);
+               grep_source_load_driver(gs, opt->repo->index);
                /*
                 * We might set up the shared textconv cache data here, which
                 * is not thread-safe.
@@ -1818,11 +1822,11 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
        if (!textconv) {
                switch (opt->binary) {
                case GREP_BINARY_DEFAULT:
-                       if (grep_source_is_binary(gs))
+                       if (grep_source_is_binary(gs, opt->repo->index))
                                binary_match_only = 1;
                        break;
                case GREP_BINARY_NOMATCH:
-                       if (grep_source_is_binary(gs))
+                       if (grep_source_is_binary(gs, opt->repo->index))
                                return 0; /* Assume unmatch */
                        break;
                case GREP_BINARY_TEXT:
@@ -1837,7 +1841,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
 
        try_lookahead = should_lookahead(opt);
 
-       if (fill_textconv_grep(textconv, gs) < 0)
+       if (fill_textconv_grep(opt->repo, textconv, gs) < 0)
                return 0;
 
        bol = gs->buf;
@@ -2168,22 +2172,24 @@ static int grep_source_load(struct grep_source *gs)
        BUG("invalid grep_source type to load");
 }
 
-void grep_source_load_driver(struct grep_source *gs)
+void grep_source_load_driver(struct grep_source *gs,
+                            struct index_state *istate)
 {
        if (gs->driver)
                return;
 
        grep_attr_lock();
        if (gs->path)
-               gs->driver = userdiff_find_by_path(gs->path);
+               gs->driver = userdiff_find_by_path(istate, gs->path);
        if (!gs->driver)
                gs->driver = userdiff_find_by_name("default");
        grep_attr_unlock();
 }
 
-static int grep_source_is_binary(struct grep_source *gs)
+static int grep_source_is_binary(struct grep_source *gs,
+                                struct index_state *istate)
 {
-       grep_source_load_driver(gs);
+       grep_source_load_driver(gs, istate);
        if (gs->driver->binary != -1)
                return gs->driver->binary;
 
diff --git a/grep.h b/grep.h
index 0ba62a11c5cc2767b15b52aca20a0ea5b00870b8..1a57d12b906fb07a9041f52b109dcdb4c7478bfa 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -36,6 +36,8 @@ typedef int pcre2_jit_stack;
 #include "thread-utils.h"
 #include "userdiff.h"
 
+struct repository;
+
 enum grep_pat_token {
        GREP_PATTERN,
        GREP_PATTERN_HEAD,
@@ -136,6 +138,7 @@ struct grep_opt {
        struct grep_pat *header_list;
        struct grep_pat **header_tail;
        struct grep_expr *pattern_expression;
+       struct repository *repo;
        const char *prefix;
        int prefix_length;
        regex_t regexp;
@@ -183,9 +186,9 @@ struct grep_opt {
        void *output_priv;
 };
 
-extern void init_grep_defaults(void);
+extern void init_grep_defaults(struct repository *);
 extern int grep_config(const char *var, const char *value, void *);
-extern void grep_init(struct grep_opt *, const char *prefix);
+extern void grep_init(struct grep_opt *, struct repository *repo, const char *prefix);
 void grep_commit_pattern_type(enum grep_pattern_type, struct grep_opt *opt);
 
 extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
@@ -217,7 +220,8 @@ void grep_source_init(struct grep_source *gs, enum grep_source_type type,
                      const void *identifier);
 void grep_source_clear_data(struct grep_source *gs);
 void grep_source_clear(struct grep_source *gs);
-void grep_source_load_driver(struct grep_source *gs);
+void grep_source_load_driver(struct grep_source *gs,
+                            struct index_state *istate);
 
 
 int grep_source(struct grep_opt *opt, struct grep_source *gs);
diff --git a/help.c b/help.c
index 96f6d221edc30c3bb8241032b837114693b53a53..4745b322993e6a3c32fa4e2ab8adad9f159193c1 100644 (file)
--- a/help.c
+++ b/help.c
@@ -98,7 +98,8 @@ static int cmd_name_cmp(const void *elem1, const void *elem2)
        return strcmp(e1->name, e2->name);
 }
 
-static void print_cmd_by_category(const struct category_description *catdesc)
+static void print_cmd_by_category(const struct category_description *catdesc,
+                                 int *longest_p)
 {
        struct cmdname_help *cmds;
        int longest = 0;
@@ -124,6 +125,8 @@ static void print_cmd_by_category(const struct category_description *catdesc)
                print_command_list(cmds, mask, longest);
        }
        free(cmds);
+       if (longest_p)
+               *longest_p = longest;
 }
 
 void add_cmdname(struct cmdnames *cmds, const char *name, int len)
@@ -307,7 +310,7 @@ void list_commands(unsigned int colopts,
 void list_common_cmds_help(void)
 {
        puts(_("These are common Git commands used in various situations:"));
-       print_cmd_by_category(common_categories);
+       print_cmd_by_category(common_categories, NULL);
 }
 
 void list_all_main_cmds(struct string_list *list)
@@ -405,7 +408,7 @@ void list_common_guides_help(void)
                { CAT_guide, N_("The common Git guides are:") },
                { 0, NULL }
        };
-       print_cmd_by_category(catdesc);
+       print_cmd_by_category(catdesc, NULL);
        putchar('\n');
 }
 
@@ -494,9 +497,48 @@ void list_config_help(int for_human)
        string_list_clear(&keys, 0);
 }
 
+static int get_alias(const char *var, const char *value, void *data)
+{
+       struct string_list *list = data;
+
+       if (skip_prefix(var, "alias.", &var))
+               string_list_append(list, var)->util = xstrdup(value);
+
+       return 0;
+}
+
 void list_all_cmds_help(void)
 {
-       print_cmd_by_category(main_categories);
+       struct string_list others = STRING_LIST_INIT_DUP;
+       struct string_list alias_list = STRING_LIST_INIT_DUP;
+       struct cmdname_help *aliases;
+       int i, longest;
+
+       printf_ln(_("See 'git help <command>' to read about a specific subcommand"));
+       print_cmd_by_category(main_categories, &longest);
+
+       list_all_other_cmds(&others);
+       if (others.nr)
+               printf("\n%s\n", _("External commands"));
+       for (i = 0; i < others.nr; i++)
+               printf("   %s\n", others.items[i].string);
+       string_list_clear(&others, 0);
+
+       git_config(get_alias, &alias_list);
+       string_list_sort(&alias_list);
+       if (alias_list.nr) {
+               printf("\n%s\n", _("Command aliases"));
+               ALLOC_ARRAY(aliases, alias_list.nr + 1);
+               for (i = 0; i < alias_list.nr; i++) {
+                       aliases[i].name = alias_list.items[i].string;
+                       aliases[i].help = alias_list.items[i].util;
+                       aliases[i].category = 1;
+               }
+               aliases[alias_list.nr].name = NULL;
+               print_command_list(aliases, 1, longest);
+               free(aliases);
+       }
+       string_list_clear(&alias_list, 1);
 }
 
 int is_in_cmdlist(struct cmdnames *c, const char *s)
index 1bbb0cdb6d03353fbc145d6ba748a30c9eabcec0..d1f52cbdf698ef3ac48d3c599d28d6ba7dfa13aa 100644 (file)
@@ -1925,7 +1925,7 @@ int cmd_main(int argc, const char **argv)
                if (!push_all && !is_null_oid(&ref->old_oid))
                        argv_array_pushf(&commit_argv, "^%s",
                                         oid_to_hex(&ref->old_oid));
-               init_revisions(&revs, setup_git_directory());
+               repo_init_revisions(the_repository, &revs, setup_git_directory());
                setup_revisions(commit_argv.argc, commit_argv.argv, &revs, NULL);
                revs.edge_hint = 0; /* just in case */
 
diff --git a/http.c b/http.c
index 98ff122585c4da84894ccae97db54859e45f3bec..3dc8c560d65e7acebb22f63e797406e38afc6422 100644 (file)
--- a/http.c
+++ b/http.c
@@ -155,6 +155,16 @@ static struct active_request_slot *active_queue_head;
 
 static char *cached_accept_language;
 
+static char *http_ssl_backend;
+
+static int http_schannel_check_revoke = 1;
+/*
+ * With the backend being set to `schannel`, setting sslCAinfo would override
+ * the Certificate Store in cURL v7.60.0 and later, which is not what we want
+ * by default.
+ */
+static int http_schannel_use_ssl_cainfo;
+
 size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
 {
        size_t size = eltsize * nmemb;
@@ -302,6 +312,22 @@ static int http_options(const char *var, const char *value, void *cb)
                curl_ssl_try = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp("http.sslbackend", var)) {
+               free(http_ssl_backend);
+               http_ssl_backend = xstrdup_or_null(value);
+               return 0;
+       }
+
+       if (!strcmp("http.schannelcheckrevoke", var)) {
+               http_schannel_check_revoke = git_config_bool(var, value);
+               return 0;
+       }
+
+       if (!strcmp("http.schannelusesslcainfo", var)) {
+               http_schannel_use_ssl_cainfo = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp("http.minsessions", var)) {
                min_curl_sessions = git_config_int(var, value);
 #ifndef USE_CURL_MULTI
@@ -803,6 +829,15 @@ static CURL *get_curl_handle(void)
        }
 #endif
 
+       if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
+           !http_schannel_check_revoke) {
+#if LIBCURL_VERSION_NUM >= 0x072c00
+               curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
+#else
+               warning(_("CURLSSLOPT_NO_REVOKE not suported with cURL < 7.44.0"));
+#endif
+       }
+
        if (http_proactive_auth)
                init_curl_http_auth(result);
 
@@ -844,7 +879,13 @@ static CURL *get_curl_handle(void)
        if (ssl_pinnedkey != NULL)
                curl_easy_setopt(result, CURLOPT_PINNEDPUBLICKEY, ssl_pinnedkey);
 #endif
-       if (ssl_cainfo != NULL)
+       if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
+           !http_schannel_use_ssl_cainfo) {
+               curl_easy_setopt(result, CURLOPT_CAINFO, NULL);
+#if LIBCURL_VERSION_NUM >= 0x073400
+               curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL);
+#endif
+       } else if (ssl_cainfo != NULL)
                curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
 
        if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
@@ -866,8 +907,7 @@ static CURL *get_curl_handle(void)
        curl_easy_setopt(result, CURLOPT_PROTOCOLS,
                         get_curl_allowed_protocols(-1));
 #else
-       warning("protocol restrictions not applied to curl redirects because\n"
-               "your curl version is too old (>= 7.19.4)");
+       warning(_("Protocol restrictions not supported with cURL < 7.19.4"));
 #endif
        if (getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
@@ -995,6 +1035,33 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
        git_config(urlmatch_config_entry, &config);
        free(normalized_url);
 
+#if LIBCURL_VERSION_NUM >= 0x073800
+       if (http_ssl_backend) {
+               const curl_ssl_backend **backends;
+               struct strbuf buf = STRBUF_INIT;
+               int i;
+
+               switch (curl_global_sslset(-1, http_ssl_backend, &backends)) {
+               case CURLSSLSET_UNKNOWN_BACKEND:
+                       strbuf_addf(&buf, _("Unsupported SSL backend '%s'. "
+                                           "Supported SSL backends:"),
+                                           http_ssl_backend);
+                       for (i = 0; backends[i]; i++)
+                               strbuf_addf(&buf, "\n\t%s", backends[i]->name);
+                       die("%s", buf.buf);
+               case CURLSSLSET_NO_BACKENDS:
+                       die(_("Could not set SSL backend to '%s': "
+                             "cURL was built without SSL backends"),
+                           http_ssl_backend);
+               case CURLSSLSET_TOO_LATE:
+                       die(_("Could not set SSL backend to '%s': already set"),
+                           http_ssl_backend);
+               case CURLSSLSET_OK:
+                       break; /* Okay! */
+               }
+       }
+#endif
+
        if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
                die("curl_global_init failed");
 
diff --git a/ident.c b/ident.c
index 327abe557f5a66546665664511d3e71b7f0929e7..33bcf40644cdf23434a7cb622b6be71bb7cd867c 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -168,6 +168,9 @@ const char *ident_default_email(void)
                        strbuf_addstr(&git_default_email, email);
                        committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
                        author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
+               } else if ((email = query_user_email()) && email[0]) {
+                       strbuf_addstr(&git_default_email, email);
+                       free((char *)email);
                } else
                        copy_email(xgetpwuid_self(&default_email_is_bogus),
                                   &git_default_email, &default_email_is_bogus);
diff --git a/khash.h b/khash.h
index 07b4cc2e6714598ef920dcf28b8f73ba34979677..532109c87f7ce190b5de3e341bafad880238ecad 100644 (file)
--- a/khash.h
+++ b/khash.h
@@ -82,11 +82,16 @@ static const double __ac_HASH_UPPER = 0.77;
        SCOPE kh_##name##_t *kh_init_##name(void) {                                                     \
                return (kh_##name##_t*)xcalloc(1, sizeof(kh_##name##_t));               \
        }                                                                                                                                       \
+       SCOPE void kh_release_##name(kh_##name##_t *h)                                          \
+       {                                                                                                                                       \
+               free(h->flags);                                                                                                 \
+               free((void *)h->keys);                                                                                  \
+               free((void *)h->vals);                                                                                  \
+       }                                                                                                                                       \
        SCOPE void kh_destroy_##name(kh_##name##_t *h)                                          \
        {                                                                                                                                       \
                if (h) {                                                                                                                \
-                       free((void *)h->keys); free(h->flags);                                  \
-                       free((void *)h->vals);                                                                          \
+                       kh_release_##name(h);                                                                           \
                        free(h);                                                                                                        \
                }                                                                                                                               \
        }                                                                                                                                       \
@@ -229,7 +234,7 @@ static const double __ac_HASH_UPPER = 0.77;
        __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
 
 #define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
-       KHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+       KHASH_INIT2(name, MAYBE_UNUSED static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
 
 /* Other convenient macros... */
 
index 72a5fed661ca0a0f0eb1fe726e91acc8cd47512a..d1d429d73818cf4ef47b3e9e64c1e39489200be5 100644 (file)
@@ -508,7 +508,9 @@ static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec)
        return;
 }
 
-static void fill_line_ends(struct diff_filespec *spec, long *lines,
+static void fill_line_ends(struct repository *r,
+                          struct diff_filespec *spec,
+                          long *lines,
                           unsigned long **line_ends)
 {
        int num = 0, size = 50;
@@ -516,7 +518,7 @@ static void fill_line_ends(struct diff_filespec *spec, long *lines,
        unsigned long *ends = NULL;
        char *data = NULL;
 
-       if (diff_populate_filespec(spec, 0))
+       if (diff_populate_filespec(r, spec, 0))
                die("Cannot read blob %s", oid_to_hex(&spec->oid));
 
        ALLOC_ARRAY(ends, size);
@@ -555,7 +557,8 @@ static const char *nth_line(void *data, long line)
 }
 
 static struct line_log_data *
-parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
+parse_lines(struct repository *r, struct commit *commit,
+           const char *prefix, struct string_list *args)
 {
        long lines = 0;
        unsigned long *ends = NULL;
@@ -571,7 +574,7 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
                long begin = 0, end = 0;
                long anchor;
 
-               name_part = skip_range_arg(item->string);
+               name_part = skip_range_arg(item->string, r->index);
                if (!name_part || *name_part != ':' || !name_part[1])
                        die("-L argument not 'start,end:file' or ':funcname:file': %s",
                            item->string);
@@ -583,7 +586,7 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
 
                spec = alloc_filespec(full_name);
                fill_blob_sha1(commit, spec);
-               fill_line_ends(spec, &lines, &ends);
+               fill_line_ends(r, spec, &lines, &ends);
                cb_data.spec = spec;
                cb_data.lines = lines;
                cb_data.line_ends = ends;
@@ -596,7 +599,7 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
 
                if (parse_range_arg(range_part, nth_line, &cb_data,
                                    lines, anchor, &begin, &end,
-                                   full_name))
+                                   full_name, r->index))
                        die("malformed -L argument '%s'", range_part);
                if ((!lines && (begin || end)) || lines < begin)
                        die("file %s has only %lu lines", name_part, lines);
@@ -739,7 +742,7 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
        struct line_log_data *range;
 
        commit = check_single_commit(rev);
-       range = parse_lines(commit, prefix, args);
+       range = parse_lines(rev->diffopt.repo, commit, prefix, args);
        add_line_range(rev, commit, range);
 
        if (!rev->diffopt.detect_rename) {
@@ -891,8 +894,8 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
                return;
 
        if (pair->one->oid_valid)
-               fill_line_ends(pair->one, &p_lines, &p_ends);
-       fill_line_ends(pair->two, &t_lines, &t_ends);
+               fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
+       fill_line_ends(rev->diffopt.repo, pair->two, &t_lines, &t_ends);
 
        fprintf(opt->file, "%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset);
        fprintf(opt->file, "%s%s--- %s%s%s\n", prefix, c_meta,
@@ -1008,12 +1011,12 @@ static int process_diff_filepair(struct rev_info *rev,
                return 0;
 
        assert(pair->two->oid_valid);
-       diff_populate_filespec(pair->two, 0);
+       diff_populate_filespec(rev->diffopt.repo, pair->two, 0);
        file_target.ptr = pair->two->data;
        file_target.size = pair->two->size;
 
        if (pair->one->oid_valid) {
-               diff_populate_filespec(pair->one, 0);
+               diff_populate_filespec(rev->diffopt.repo, pair->one, 0);
                file_parent.ptr = pair->one->data;
                file_parent.size = pair->one->size;
        } else {
index 232c3909ec27d3079e9228d0fcf561ce6a349754..9b50583dc05147fb6940c1d551ba274702704f07 100644 (file)
@@ -163,9 +163,10 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
        }
 }
 
-static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_cb,
-                                       void *cb_data, long lines, long anchor, long *begin, long *end,
-                                       const char *path)
+static const char *parse_range_funcname(
+       const char *arg, nth_line_fn_t nth_line_cb,
+       void *cb_data, long lines, long anchor, long *begin, long *end,
+       const char *path, struct index_state *istate)
 {
        char *pattern;
        const char *term;
@@ -198,7 +199,7 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_
        anchor--; /* input is in human terms */
        start = nth_line_cb(cb_data, anchor);
 
-       drv = userdiff_find_by_path(path);
+       drv = userdiff_find_by_path(istate, path);
        if (drv && drv->funcname.pattern) {
                const struct userdiff_funcname *pe = &drv->funcname;
                xecfg = xcalloc(1, sizeof(*xecfg));
@@ -244,7 +245,8 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_
 
 int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
                    void *cb_data, long lines, long anchor,
-                   long *begin, long *end, const char *path)
+                   long *begin, long *end,
+                   const char *path, struct index_state *istate)
 {
        *begin = *end = 0;
 
@@ -254,7 +256,9 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
                anchor = lines + 1;
 
        if (*arg == ':' || (*arg == '^' && *(arg + 1) == ':')) {
-               arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, anchor, begin, end, path);
+               arg = parse_range_funcname(arg, nth_line_cb, cb_data,
+                                          lines, anchor, begin, end,
+                                          path, istate);
                if (!arg || *arg)
                        return -1;
                return 0;
@@ -275,10 +279,12 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
        return 0;
 }
 
-const char *skip_range_arg(const char *arg)
+const char *skip_range_arg(const char *arg, struct index_state *istate)
 {
        if (*arg == ':' || (*arg == '^' && *(arg + 1) == ':'))
-               return parse_range_funcname(arg, NULL, NULL, 0, 0, NULL, NULL, NULL);
+               return parse_range_funcname(arg, NULL, NULL,
+                                           0, 0, NULL, NULL,
+                                           NULL, istate);
 
        arg = parse_loc(arg, NULL, NULL, 0, -1, NULL);
 
index d3c54e45aa8a8e488778bbc7db1c6a5c0f602ab2..e69bf7c01765bba0b54a7a4174706f1258e0fdb0 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef LINE_RANGE_H
 #define LINE_RANGE_H
 
+struct index_state;
+
 /*
  * Parse one item in an -L begin,end option w.r.t. the notional file
  * object 'cb_data' consisting of 'lines' lines.
@@ -23,7 +25,7 @@ int parse_range_arg(const char *arg,
                    nth_line_fn_t nth_line_cb,
                    void *cb_data, long lines, long anchor,
                    long *begin, long *end,
-                   const char *path);
+                   const char *path, struct index_state *istate);
 
 /*
  * Scan past a range argument that could be parsed by
@@ -34,6 +36,6 @@ int parse_range_arg(const char *arg,
  * NULL in case the argument is obviously malformed.
  */
 
-const char *skip_range_arg(const char *arg);
+const char *skip_range_arg(const char *arg, struct index_state *istate);
 
 #endif /* LINE_RANGE_H */
index c0e2bd6a06a27b6056364c43b003d74539fd6cad..e8da2e8581b2df956411f19e796ceb94adcef903 100644 (file)
@@ -30,7 +30,6 @@ static int gently_parse_list_objects_filter(
 
        if (filter_options->choice) {
                if (errbuf) {
-                       strbuf_init(errbuf, 0);
                        strbuf_addstr(
                                errbuf,
                                _("multiple filter-specs cannot be combined"));
@@ -50,6 +49,19 @@ static int gently_parse_list_objects_filter(
                        return 0;
                }
 
+       } else if (skip_prefix(arg, "tree:", &v0)) {
+               unsigned long depth;
+               if (!git_parse_ulong(v0, &depth) || depth != 0) {
+                       if (errbuf) {
+                               strbuf_addstr(
+                                       errbuf,
+                                       _("only 'tree:0' is supported"));
+                       }
+                       return 1;
+               }
+               filter_options->choice = LOFC_TREE_NONE;
+               return 0;
+
        } else if (skip_prefix(arg, "sparse:oid=", &v0)) {
                struct object_context oc;
                struct object_id sparse_oid;
@@ -71,10 +83,9 @@ static int gently_parse_list_objects_filter(
                return 0;
        }
 
-       if (errbuf) {
-               strbuf_init(errbuf, 0);
+       if (errbuf)
                strbuf_addf(errbuf, "invalid filter-spec '%s'", arg);
-       }
+
        memset(filter_options, 0, sizeof(*filter_options));
        return 1;
 }
index 0000a61f82d3dcc36e190b5422a13c93d47f15c2..af64e5c66fc4a242f9cc534380a9b3e8f1e67d00 100644 (file)
@@ -10,6 +10,7 @@ enum list_objects_filter_choice {
        LOFC_DISABLED = 0,
        LOFC_BLOB_NONE,
        LOFC_BLOB_LIMIT,
+       LOFC_TREE_NONE,
        LOFC_SPARSE_OID,
        LOFC_SPARSE_PATH,
        LOFC__COUNT /* must be last */
index a0ba78b20cc99bfcd2c41abd4f312b5649a2b9cd..765f3df3b067c5df7b98402ef4fc953edcd9179b 100644 (file)
@@ -44,8 +44,7 @@ static enum list_objects_filter_result filter_blobs_none(
 
        switch (filter_situation) {
        default:
-               die("unknown filter_situation");
-               return LOFR_ZERO;
+               BUG("unknown filter_situation: %d", filter_situation);
 
        case LOFS_BEGIN_TREE:
                assert(obj->type == OBJ_TREE);
@@ -80,6 +79,61 @@ static void *filter_blobs_none__init(
        return d;
 }
 
+/*
+ * 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 oidset *omits;
+};
+
+static enum list_objects_filter_result filter_trees_none(
+       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_;
+
+       switch (filter_situation) {
+       default:
+               BUG("unknown filter_situation: %d", filter_situation);
+
+       case LOFS_BEGIN_TREE:
+       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;
+               } else {
+                       /*
+                        * Not collecting omits so no need to to traverse tree.
+                        */
+                       return LOFR_SKIP_TREE | LOFR_MARK_SEEN;
+               }
+
+       case LOFS_END_TREE:
+               assert(obj->type == OBJ_TREE);
+               return LOFR_ZERO;
+
+       }
+}
+
+static void* filter_trees_none__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));
+       d->omits = omitted;
+
+       *filter_fn = filter_trees_none;
+       *filter_free_fn = free;
+       return d;
+}
+
 /*
  * A filter for list-objects to omit large blobs.
  * And to OPTIONALLY collect a list of the omitted OIDs.
@@ -102,8 +156,7 @@ static enum list_objects_filter_result filter_blobs_limit(
 
        switch (filter_situation) {
        default:
-               die("unknown filter_situation");
-               return LOFR_ZERO;
+               BUG("unknown filter_situation: %d", filter_situation);
 
        case LOFS_BEGIN_TREE:
                assert(obj->type == OBJ_TREE);
@@ -208,8 +261,7 @@ static enum list_objects_filter_result filter_sparse(
 
        switch (filter_situation) {
        default:
-               die("unknown filter_situation");
-               return LOFR_ZERO;
+               BUG("unknown filter_situation: %d", filter_situation);
 
        case LOFS_BEGIN_TREE:
                assert(obj->type == OBJ_TREE);
@@ -374,6 +426,7 @@ static filter_init_fn s_filters[] = {
        NULL,
        filter_blobs_none__init,
        filter_blobs_limit__init,
+       filter_trees_none__init,
        filter_sparse_oid__init,
        filter_sparse_path__init,
 };
@@ -389,7 +442,7 @@ void *list_objects_filter__init(
        assert((sizeof(s_filters) / sizeof(s_filters[0])) == LOFC__COUNT);
 
        if (filter_options->choice >= LOFC__COUNT)
-               die("invalid list-objects filter choice: %d",
+               BUG("invalid list-objects filter choice: %d",
                    filter_options->choice);
 
        init_fn = s_filters[filter_options->choice];
index a6f6b4990b43c8f4c8cb94ba5b890370287ec868..52b4a84da9418497a4fc37e50cb8e0dcbc5b70bd 100644 (file)
@@ -24,6 +24,11 @@ struct oidset;
  *              In general, objects should only be shown once, but
  *              this result DOES NOT imply that we mark it SEEN.
  *
+ * _SKIP_TREE : Used in LOFS_BEGIN_TREE situation - indicates that
+ *              the tree's children should not be iterated over. This
+ *              is used as an optimization when all children will
+ *              definitely be ignored.
+ *
  * Most of the time, you want the combination (_MARK_SEEN | _DO_SHOW)
  * but they can be used independently, such as when sparse-checkout
  * pattern matching is being applied.
@@ -45,6 +50,7 @@ enum list_objects_filter_result {
        LOFR_ZERO      = 0,
        LOFR_MARK_SEEN = 1<<0,
        LOFR_DO_SHOW   = 1<<1,
+       LOFR_SKIP_TREE = 1<<2,
 };
 
 enum list_objects_filter_situation {
index c99c47ac181612db5122a313557090e1957fea4f..c41cc80db5bc86279bcf560109f599a9783b2755 100644 (file)
 #include "list-objects-filter-options.h"
 #include "packfile.h"
 #include "object-store.h"
+#include "trace.h"
 
-static void process_blob(struct rev_info *revs,
+struct traversal_context {
+       struct rev_info *revs;
+       show_object_fn show_object;
+       show_commit_fn show_commit;
+       void *show_data;
+       filter_object_fn filter_fn;
+       void *filter_data;
+};
+
+static void process_blob(struct traversal_context *ctx,
                         struct blob *blob,
-                        show_object_fn show,
                         struct strbuf *path,
-                        const char *name,
-                        void *cb_data,
-                        filter_object_fn filter_fn,
-                        void *filter_data)
+                        const char *name)
 {
        struct object *obj = &blob->object;
        size_t pathlen;
        enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
 
-       if (!revs->blob_objects)
+       if (!ctx->revs->blob_objects)
                return;
        if (!obj)
                die("bad blob object");
@@ -41,21 +47,21 @@ static void process_blob(struct rev_info *revs,
         * may cause the actual filter to report an incomplete list
         * of missing objects.
         */
-       if (revs->exclude_promisor_objects &&
+       if (ctx->revs->exclude_promisor_objects &&
            !has_object_file(&obj->oid) &&
            is_promisor_object(&obj->oid))
                return;
 
        pathlen = path->len;
        strbuf_addstr(path, name);
-       if (!(obj->flags & USER_GIVEN) && filter_fn)
-               r = filter_fn(LOFS_BLOB, obj,
-                             path->buf, &path->buf[pathlen],
-                             filter_data);
+       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn)
+               r = ctx->filter_fn(LOFS_BLOB, obj,
+                                  path->buf, &path->buf[pathlen],
+                                  ctx->filter_data);
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
-               show(obj, path->buf, cb_data);
+               ctx->show_object(obj, path->buf, ctx->show_data);
        strbuf_setlen(path, pathlen);
 }
 
@@ -81,34 +87,66 @@ static void process_blob(struct rev_info *revs,
  * the link, and how to do it. Whether it necessarily makes
  * any sense what-so-ever to ever do that is another issue.
  */
-static void process_gitlink(struct rev_info *revs,
+static void process_gitlink(struct traversal_context *ctx,
                            const unsigned char *sha1,
-                           show_object_fn show,
                            struct strbuf *path,
-                           const char *name,
-                           void *cb_data)
+                           const char *name)
 {
        /* Nothing to do */
 }
 
-static void process_tree(struct rev_info *revs,
+static void process_tree(struct traversal_context *ctx,
                         struct tree *tree,
-                        show_object_fn show,
                         struct strbuf *base,
-                        const char *name,
-                        void *cb_data,
-                        filter_object_fn filter_fn,
-                        void *filter_data)
+                        const char *name);
+
+static void process_tree_contents(struct traversal_context *ctx,
+                                 struct tree *tree,
+                                 struct strbuf *base)
 {
-       struct object *obj = &tree->object;
        struct tree_desc desc;
        struct name_entry entry;
-       enum interesting match = revs->diffopt.pathspec.nr == 0 ?
-               all_entries_interesting: entry_not_interesting;
+       enum interesting match = ctx->revs->diffopt.pathspec.nr == 0 ?
+               all_entries_interesting : entry_not_interesting;
+
+       init_tree_desc(&desc, tree->buffer, tree->size);
+
+       while (tree_entry(&desc, &entry)) {
+               if (match != all_entries_interesting) {
+                       match = tree_entry_interesting(&entry, base, 0,
+                                                      &ctx->revs->diffopt.pathspec);
+                       if (match == all_entries_not_interesting)
+                               break;
+                       if (match == entry_not_interesting)
+                               continue;
+               }
+
+               if (S_ISDIR(entry.mode)) {
+                       struct tree *t = lookup_tree(the_repository, 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,
+                                       base, entry.path);
+               else {
+                       struct blob *b = lookup_blob(the_repository, entry.oid);
+                       b->object.flags |= NOT_USER_GIVEN;
+                       process_blob(ctx, b, base, entry.path);
+               }
+       }
+}
+
+static void process_tree(struct traversal_context *ctx,
+                        struct tree *tree,
+                        struct strbuf *base,
+                        const char *name)
+{
+       struct object *obj = &tree->object;
+       struct rev_info *revs = ctx->revs;
        int baselen = base->len;
        enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
-       int gently = revs->ignore_missing_links ||
-                    revs->exclude_promisor_objects;
+       int failed_parse;
 
        if (!revs->tree_objects)
                return;
@@ -116,7 +154,9 @@ static void process_tree(struct rev_info *revs,
                die("bad tree object");
        if (obj->flags & (UNINTERESTING | SEEN))
                return;
-       if (parse_tree_gently(tree, gently) < 0) {
+
+       failed_parse = parse_tree_gently(tree, 1);
+       if (failed_parse) {
                if (revs->ignore_missing_links)
                        return;
 
@@ -129,57 +169,35 @@ static void process_tree(struct rev_info *revs,
                    is_promisor_object(&obj->oid))
                        return;
 
-               die("bad tree object %s", oid_to_hex(&obj->oid));
+               if (!revs->do_not_die_on_missing_tree)
+                       die("bad tree object %s", oid_to_hex(&obj->oid));
        }
 
        strbuf_addstr(base, name);
-       if (!(obj->flags & USER_GIVEN) && filter_fn)
-               r = filter_fn(LOFS_BEGIN_TREE, obj,
-                             base->buf, &base->buf[baselen],
-                             filter_data);
+       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn)
+               r = ctx->filter_fn(LOFS_BEGIN_TREE, obj,
+                                  base->buf, &base->buf[baselen],
+                                  ctx->filter_data);
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
-               show(obj, base->buf, cb_data);
+               ctx->show_object(obj, base->buf, ctx->show_data);
        if (base->len)
                strbuf_addch(base, '/');
 
-       init_tree_desc(&desc, tree->buffer, tree->size);
+       if (r & LOFR_SKIP_TREE)
+               trace_printf("Skipping contents of tree %s...\n", base->buf);
+       else if (!failed_parse)
+               process_tree_contents(ctx, tree, base);
 
-       while (tree_entry(&desc, &entry)) {
-               if (match != all_entries_interesting) {
-                       match = tree_entry_interesting(&entry, base, 0,
-                                                      &revs->diffopt.pathspec);
-                       if (match == all_entries_not_interesting)
-                               break;
-                       if (match == entry_not_interesting)
-                               continue;
-               }
-
-               if (S_ISDIR(entry.mode))
-                       process_tree(revs,
-                                    lookup_tree(the_repository, entry.oid),
-                                    show, base, entry.path,
-                                    cb_data, filter_fn, filter_data);
-               else if (S_ISGITLINK(entry.mode))
-                       process_gitlink(revs, entry.oid->hash,
-                                       show, base, entry.path,
-                                       cb_data);
-               else
-                       process_blob(revs,
-                                    lookup_blob(the_repository, entry.oid),
-                                    show, base, entry.path,
-                                    cb_data, filter_fn, filter_data);
-       }
-
-       if (!(obj->flags & USER_GIVEN) && filter_fn) {
-               r = filter_fn(LOFS_END_TREE, obj,
-                             base->buf, &base->buf[baselen],
-                             filter_data);
+       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn) {
+               r = ctx->filter_fn(LOFS_END_TREE, obj,
+                                  base->buf, &base->buf[baselen],
+                                  ctx->filter_data);
                if (r & LOFR_MARK_SEEN)
                        obj->flags |= SEEN;
                if (r & LOFR_DO_SHOW)
-                       show(obj, base->buf, cb_data);
+                       ctx->show_object(obj, base->buf, ctx->show_data);
        }
 
        strbuf_setlen(base, baselen);
@@ -196,7 +214,7 @@ static void mark_edge_parents_uninteresting(struct commit *commit,
                struct commit *parent = parents->item;
                if (!(parent->object.flags & UNINTERESTING))
                        continue;
-               mark_tree_uninteresting(get_commit_tree(parent));
+               mark_tree_uninteresting(revs->repo, get_commit_tree(parent));
                if (revs->edge_hint && !(parent->object.flags & SHOWN)) {
                        parent->object.flags |= SHOWN;
                        show_edge(parent);
@@ -213,7 +231,8 @@ void mark_edges_uninteresting(struct rev_info *revs, show_edge_fn show_edge)
                struct commit *commit = list->item;
 
                if (commit->object.flags & UNINTERESTING) {
-                       mark_tree_uninteresting(get_commit_tree(commit));
+                       mark_tree_uninteresting(revs->repo,
+                                               get_commit_tree(commit));
                        if (revs->edge_hint_aggressive && !(commit->object.flags & SHOWN)) {
                                commit->object.flags |= SHOWN;
                                show_edge(commit);
@@ -228,7 +247,8 @@ void mark_edges_uninteresting(struct rev_info *revs, show_edge_fn show_edge)
                        struct commit *commit = (struct commit *)obj;
                        if (obj->type != OBJ_COMMIT || !(obj->flags & UNINTERESTING))
                                continue;
-                       mark_tree_uninteresting(get_commit_tree(commit));
+                       mark_tree_uninteresting(revs->repo,
+                                               get_commit_tree(commit));
                        if (!(obj->flags & SHOWN)) {
                                obj->flags |= SHOWN;
                                show_edge(commit);
@@ -242,19 +262,15 @@ static void add_pending_tree(struct rev_info *revs, struct tree *tree)
        add_pending_object(revs, &tree->object, "");
 }
 
-static void traverse_trees_and_blobs(struct rev_info *revs,
-                                    struct strbuf *base,
-                                    show_object_fn show_object,
-                                    void *show_data,
-                                    filter_object_fn filter_fn,
-                                    void *filter_data)
+static void traverse_trees_and_blobs(struct traversal_context *ctx,
+                                    struct strbuf *base)
 {
        int i;
 
        assert(base->len == 0);
 
-       for (i = 0; i < revs->pending.nr; i++) {
-               struct object_array_entry *pending = revs->pending.objects + i;
+       for (i = 0; i < ctx->revs->pending.nr; i++) {
+               struct object_array_entry *pending = ctx->revs->pending.objects + i;
                struct object *obj = pending->item;
                const char *name = pending->name;
                const char *path = pending->path;
@@ -262,62 +278,52 @@ static void traverse_trees_and_blobs(struct rev_info *revs,
                        continue;
                if (obj->type == OBJ_TAG) {
                        obj->flags |= SEEN;
-                       show_object(obj, name, show_data);
+                       ctx->show_object(obj, name, ctx->show_data);
                        continue;
                }
                if (!path)
                        path = "";
                if (obj->type == OBJ_TREE) {
-                       process_tree(revs, (struct tree *)obj, show_object,
-                                    base, path, show_data,
-                                    filter_fn, filter_data);
+                       process_tree(ctx, (struct tree *)obj, base, path);
                        continue;
                }
                if (obj->type == OBJ_BLOB) {
-                       process_blob(revs, (struct blob *)obj, show_object,
-                                    base, path, show_data,
-                                    filter_fn, filter_data);
+                       process_blob(ctx, (struct blob *)obj, base, path);
                        continue;
                }
                die("unknown pending object %s (%s)",
                    oid_to_hex(&obj->oid), name);
        }
-       object_array_clear(&revs->pending);
+       object_array_clear(&ctx->revs->pending);
 }
 
-static void do_traverse(struct rev_info *revs,
-                       show_commit_fn show_commit,
-                       show_object_fn show_object,
-                       void *show_data,
-                       filter_object_fn filter_fn,
-                       void *filter_data)
+static void do_traverse(struct traversal_context *ctx)
 {
        struct commit *commit;
        struct strbuf csp; /* callee's scratch pad */
        strbuf_init(&csp, PATH_MAX);
 
-       while ((commit = get_revision(revs)) != NULL) {
+       while ((commit = get_revision(ctx->revs)) != NULL) {
                /*
                 * an uninteresting boundary commit may not have its tree
                 * parsed yet, but we are not going to show them anyway
                 */
-               if (get_commit_tree(commit))
-                       add_pending_tree(revs, get_commit_tree(commit));
-               show_commit(commit, show_data);
+               if (get_commit_tree(commit)) {
+                       struct tree *tree = get_commit_tree(commit);
+                       tree->object.flags |= NOT_USER_GIVEN;
+                       add_pending_tree(ctx->revs, tree);
+               }
+               ctx->show_commit(commit, ctx->show_data);
 
-               if (revs->tree_blobs_in_commit_order)
+               if (ctx->revs->tree_blobs_in_commit_order)
                        /*
                         * NEEDSWORK: Adding the tree and then flushing it here
                         * needs a reallocation for each commit. Can we pass the
                         * tree directory without allocation churn?
                         */
-                       traverse_trees_and_blobs(revs, &csp,
-                                                show_object, show_data,
-                                                filter_fn, filter_data);
+                       traverse_trees_and_blobs(ctx, &csp);
        }
-       traverse_trees_and_blobs(revs, &csp,
-                                show_object, show_data,
-                                filter_fn, filter_data);
+       traverse_trees_and_blobs(ctx, &csp);
        strbuf_release(&csp);
 }
 
@@ -326,7 +332,14 @@ void traverse_commit_list(struct rev_info *revs,
                          show_object_fn show_object,
                          void *show_data)
 {
-       do_traverse(revs, show_commit, show_object, show_data, NULL, NULL);
+       struct traversal_context ctx;
+       ctx.revs = revs;
+       ctx.show_commit = show_commit;
+       ctx.show_object = show_object;
+       ctx.show_data = show_data;
+       ctx.filter_fn = NULL;
+       ctx.filter_data = NULL;
+       do_traverse(&ctx);
 }
 
 void traverse_commit_list_filtered(
@@ -337,14 +350,18 @@ void traverse_commit_list_filtered(
        void *show_data,
        struct oidset *omitted)
 {
-       filter_object_fn filter_fn = NULL;
+       struct traversal_context ctx;
        filter_free_fn filter_free_fn = NULL;
-       void *filter_data = NULL;
-
-       filter_data = list_objects_filter__init(omitted, filter_options,
-                                               &filter_fn, &filter_free_fn);
-       do_traverse(revs, show_commit, show_object, show_data,
-                   filter_fn, filter_data);
-       if (filter_data && filter_free_fn)
-               filter_free_fn(filter_data);
+
+       ctx.revs = revs;
+       ctx.show_object = show_object;
+       ctx.show_commit = show_commit;
+       ctx.show_data = show_data;
+       ctx.filter_fn = NULL;
+
+       ctx.filter_data = list_objects_filter__init(omitted, filter_options,
+                                                   &ctx.filter_fn, &filter_free_fn);
+       do_traverse(&ctx);
+       if (ctx.filter_data && filter_free_fn)
+               filter_free_fn(ctx.filter_data);
 }
index 1936fee9e1c54e8c0dc049ba48ef1cd209967d31..3c8fb917e9748b1950d5df8c85aa310149f1f8df 100644 (file)
@@ -336,10 +336,10 @@ static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr
        return &ll_merge_drv[LL_TEXT_MERGE];
 }
 
-static void normalize_file(mmfile_t *mm, const char *path)
+static void normalize_file(mmfile_t *mm, const char *path, struct index_state *istate)
 {
        struct strbuf strbuf = STRBUF_INIT;
-       if (renormalize_buffer(&the_index, path, mm->ptr, mm->size, &strbuf)) {
+       if (renormalize_buffer(istate, path, mm->ptr, mm->size, &strbuf)) {
                free(mm->ptr);
                mm->size = strbuf.len;
                mm->ptr = strbuf_detach(&strbuf, NULL);
@@ -351,6 +351,7 @@ int ll_merge(mmbuffer_t *result_buf,
             mmfile_t *ancestor, const char *ancestor_label,
             mmfile_t *ours, const char *our_label,
             mmfile_t *theirs, const char *their_label,
+            struct index_state *istate,
             const struct ll_merge_options *opts)
 {
        static struct attr_check *check;
@@ -363,15 +364,15 @@ int ll_merge(mmbuffer_t *result_buf,
                opts = &default_opts;
 
        if (opts->renormalize) {
-               normalize_file(ancestor, path);
-               normalize_file(ours, path);
-               normalize_file(theirs, path);
+               normalize_file(ancestor, path, istate);
+               normalize_file(ours, path, istate);
+               normalize_file(theirs, path, istate);
        }
 
        if (!check)
                check = attr_check_initl("merge", "conflict-marker-size", NULL);
 
-       git_check_attr(&the_index, path, check);
+       git_check_attr(istate, path, check);
        ll_driver_name = check->items[0].value;
        if (check->items[1].value) {
                marker_size = atoi(check->items[1].value);
@@ -390,14 +391,14 @@ int ll_merge(mmbuffer_t *result_buf,
                          opts, marker_size);
 }
 
-int ll_merge_marker_size(const char *path)
+int ll_merge_marker_size(struct index_state *istate, const char *path)
 {
        static struct attr_check *check;
        int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
 
        if (!check)
                check = attr_check_initl("conflict-marker-size", NULL);
-       git_check_attr(&the_index, path, check);
+       git_check_attr(istate, path, check);
        if (check->items[0].value) {
                marker_size = atoi(check->items[0].value);
                if (marker_size <= 0)
index b72b19921e8f33ab23aa5bb6293b9b6841035df9..6c6e22e40d9bab4b5071319acd5356993e7888b4 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "xdiff/xdiff.h"
 
+struct index_state;
+
 struct ll_merge_options {
        unsigned virtual_ancestor : 1;
        unsigned variant : 2;   /* favor ours, favor theirs, or union merge */
@@ -19,8 +21,9 @@ int ll_merge(mmbuffer_t *result_buf,
             mmfile_t *ancestor, const char *ancestor_label,
             mmfile_t *ours, const char *our_label,
             mmfile_t *theirs, const char *their_label,
+            struct index_state *istate,
             const struct ll_merge_options *opts);
 
-int ll_merge_marker_size(const char *path);
+int ll_merge_marker_size(struct index_state *istate, const char *path);
 
 #endif
index fabb8c19ce983962b45c415f66c11236fa906bfe..ee0a0e90c94682f4d990b298d27d734d01d90581 100644 (file)
@@ -29,7 +29,12 @@ static void free_mmfile(mmfile_t *f)
        free(f->ptr);
 }
 
-static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our, mmfile_t *their, unsigned long *size)
+static void *three_way_filemerge(struct index_state *istate,
+                                const char *path,
+                                mmfile_t *base,
+                                mmfile_t *our,
+                                mmfile_t *their,
+                                unsigned long *size)
 {
        int merge_status;
        mmbuffer_t res;
@@ -41,7 +46,8 @@ static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our
         * common ancestor.
         */
        merge_status = ll_merge(&res, path, base, NULL,
-                               our, ".our", their, ".their", NULL);
+                               our, ".our", their, ".their",
+                               istate, NULL);
        if (merge_status < 0)
                return NULL;
 
@@ -49,7 +55,9 @@ static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our
        return res.ptr;
 }
 
-void *merge_blobs(const char *path, struct blob *base, struct blob *our, struct blob *their, unsigned long *size)
+void *merge_blobs(struct index_state *istate, const char *path,
+                 struct blob *base, struct blob *our,
+                 struct blob *their, unsigned long *size)
 {
        void *res = NULL;
        mmfile_t f1, f2, common;
@@ -82,7 +90,7 @@ void *merge_blobs(const char *path, struct blob *base, struct blob *our, struct
                common.ptr = xstrdup("");
                common.size = 0;
        }
-       res = three_way_filemerge(path, &common, &f1, &f2, size);
+       res = three_way_filemerge(istate, path, &common, &f1, &f2, size);
        free_mmfile(&common);
 out_free_f2_f1:
        free_mmfile(&f2);
index 62b569e472450540689bbb0a4d08c1e3cfd89f12..cc31038b807fbee073a063ec83fe298c87fd2b3d 100644 (file)
@@ -1,8 +1,11 @@
 #ifndef MERGE_BLOBS_H
 #define MERGE_BLOBS_H
 
-#include "blob.h"
+struct blob;
+struct index_state;
 
-extern void *merge_blobs(const char *, struct blob *, struct blob *, struct blob *, unsigned long *);
+extern void *merge_blobs(struct index_state *, const char *,
+                        struct blob *, struct blob *,
+                        struct blob *, unsigned long *);
 
 #endif /* MERGE_BLOBS_H */
index 0684dab77965b6abd0f8429bc53cd936a826482b..acc2f64a4e9d03f49165e3c46396ce00a09dc6bd 100644 (file)
@@ -228,7 +228,26 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type,
                                              struct stage_data *src_entry1,
                                              struct stage_data *src_entry2)
 {
-       struct rename_conflict_info *ci = xcalloc(1, sizeof(struct rename_conflict_info));
+       struct rename_conflict_info *ci;
+
+       /*
+        * When we have two renames involved, 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 (dst_entry2 && branch1 != o->branch1) {
+               setup_rename_conflict_info(rename_type,
+                                          pair2,      pair1,
+                                          branch2,    branch1,
+                                          dst_entry2, dst_entry1,
+                                          o,
+                                          src_entry2, src_entry1);
+               return;
+       }
+
+       ci = xcalloc(1, sizeof(struct rename_conflict_info));
        ci->rename_type = rename_type;
        ci->pair1 = pair1;
        ci->branch1 = branch1;
@@ -1084,7 +1103,8 @@ static int merge_3way(struct merge_options *o,
        read_mmblob(&src2, &b->oid);
 
        merge_status = ll_merge(result_buf, a->path, &orig, base_name,
-                               &src1, name1, &src2, name2, &ll_opts);
+                               &src1, name1, &src2, name2,
+                               &the_index, &ll_opts);
 
        free(base_name);
        free(name1);
@@ -1115,7 +1135,7 @@ static int find_first_merges(struct object_array *result, const char *path,
        /* get all revisions that merge commit a */
        xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
                  oid_to_hex(&a->object.oid));
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        rev_opts.submodule = path;
        /* FIXME: can't handle linked worktrees in submodules yet */
        revs.single_worktree = path != NULL;
@@ -1283,6 +1303,17 @@ static int merge_mode_and_contents(struct merge_options *o,
                                   const char *branch2,
                                   struct merge_file_info *result)
 {
+       if (o->branch1 != branch1) {
+               /*
+                * It's weird getting a reverse merge with HEAD on the bottom
+                * side of the conflict markers and the other branch on the
+                * top.  Fix that.
+                */
+               return merge_mode_and_contents(o, one, b, a,
+                                              filename,
+                                              branch2, branch1, result);
+       }
+
        result->merge = 0;
        result->clean = 1;
 
@@ -1674,8 +1705,8 @@ static int handle_rename_rename_2to1(struct merge_options *o,
        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("%s (was %s)", path, a->path);
-       path_side_2_desc = xstrfmt("%s (was %s)", path, 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) ||
            merge_mode_and_contents(o, b, &ci->ren2_other, c2, path_side_2_desc,
@@ -1756,7 +1787,7 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
        struct diff_queue_struct *ret;
        struct diff_options opts;
 
-       diff_setup(&opts);
+       repo_diff_setup(the_repository, &opts);
        opts.flags.recursive = 1;
        opts.flags.rename_empty = 0;
        opts.detect_rename = merge_detect_rename(o);
diff --git a/merge.c b/merge.c
index e30e03fb84a7677520d208a5688bb7170c22bf8e..91008f760223e4f6c65a5d344bb6403dd5885d28 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -14,7 +14,8 @@ static const char *merge_argument(struct commit *commit)
        return oid_to_hex(commit ? &commit->object.oid : the_hash_algo->empty_tree);
 }
 
-int try_merge_command(const char *strategy, size_t xopts_nr,
+int try_merge_command(struct repository *r,
+                     const char *strategy, size_t xopts_nr,
                      const char **xopts, struct commit_list *common,
                      const char *head_arg, struct commit_list *remotes)
 {
@@ -35,15 +36,16 @@ int try_merge_command(const char *strategy, size_t xopts_nr,
        ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
        argv_array_clear(&args);
 
-       discard_cache();
-       if (read_cache() < 0)
+       discard_index(r->index);
+       if (read_index(r->index) < 0)
                die(_("failed to read the cache"));
-       resolve_undo_clear();
+       resolve_undo_clear_index(r->index);
 
        return ret;
 }
 
-int checkout_fast_forward(const struct object_id *head,
+int checkout_fast_forward(struct repository *r,
+                         const struct object_id *head,
                          const struct object_id *remote,
                          int overwrite_ignore)
 {
@@ -54,7 +56,7 @@ int checkout_fast_forward(const struct object_id *head,
        struct dir_struct dir;
        struct lock_file lock_file = LOCK_INIT;
 
-       refresh_cache(REFRESH_QUIET);
+       refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
 
        if (hold_locked_index(&lock_file, LOCK_REPORT_ON_ERROR) < 0)
                return -1;
@@ -86,8 +88,8 @@ int checkout_fast_forward(const struct object_id *head,
        }
 
        opts.head_idx = 1;
-       opts.src_index = &the_index;
-       opts.dst_index = &the_index;
+       opts.src_index = r->index;
+       opts.dst_index = r->index;
        opts.update = 1;
        opts.verbose_update = 1;
        opts.merge = 1;
@@ -101,7 +103,7 @@ int checkout_fast_forward(const struct object_id *head,
        }
        clear_unpack_trees_porcelain(&opts);
 
-       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+       if (write_locked_index(r->index, &lock_file, COMMIT_LOCK))
                return error(_("unable to write new index file"));
        return 0;
 }
diff --git a/midx.c b/midx.c
index ea2f3ffe2e21700038eabf00aa975e5cc111a47f..4fac0cd08ab9b2a78096c57518d2ea8cd1db96a2 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -941,7 +941,7 @@ static void midx_report(const char *fmt, ...)
 int verify_midx_file(const char *object_dir)
 {
        uint32_t i;
-       struct progress *progress = NULL;
+       struct progress *progress;
        struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
        verify_midx_error = 0;
 
diff --git a/midx.h b/midx.h
index 2d7c9c6cd1d829f3eb6aa924d316e54c6d4e9e00..1d6c21afe31b62024969f7cb91f9f78b19fe8e52 100644 (file)
--- a/midx.h
+++ b/midx.h
@@ -1,5 +1,5 @@
-#ifndef __MIDX_H__
-#define __MIDX_H__
+#ifndef MIDX_H
+#define MIDX_H
 
 #include "repository.h"
 
index 5764e2b0ef06a3251d93e5535e85861ad3503fe9..bd05d50b051288cb466de5afed52993a1edf3b1d 100644 (file)
@@ -127,7 +127,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));
 
-       diff_setup(&opt);
+       repo_diff_setup(the_repository, &opt);
        opt.flags.recursive = 1;
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&opt);
@@ -190,7 +190,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));
 
-       diff_setup(&opt);
+       repo_diff_setup(the_repository, &opt);
        opt.flags.recursive = 1;
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&opt);
@@ -349,7 +349,8 @@ static int ll_merge_in_worktree(struct notes_merge_options *o,
        read_mmblob(&remote, &p->remote);
 
        status = ll_merge(&result_buf, oid_to_hex(&p->obj), &base, NULL,
-                         &local, o->local_ref, &remote, o->remote_ref, NULL);
+                         &local, o->local_ref, &remote, o->remote_ref,
+                         &the_index, NULL);
 
        free(base.ptr);
        free(local.ptr);
@@ -710,7 +711,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(&blob_oid, path.buf, &st, HASH_WRITE_OBJECT))
+               if (index_path(&the_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 454c54f93396efccfb9957423931d0ea727ff340..fe4eb921df81bbabe1b95bd7f594235f360eec7d 100644 (file)
--- a/oidset.c
+++ b/oidset.c
@@ -1,40 +1,37 @@
 #include "cache.h"
 #include "oidset.h"
 
+void oidset_init(struct oidset *set, size_t initial_size)
+{
+       memset(&set->set, 0, sizeof(set->set));
+       if (initial_size)
+               kh_resize_oid(&set->set, initial_size);
+}
+
 int oidset_contains(const struct oidset *set, const struct object_id *oid)
 {
-       if (!set->map.map.tablesize)
-               return 0;
-       return !!oidmap_get(&set->map, oid);
+       khiter_t pos = kh_get_oid(&set->set, *oid);
+       return pos != kh_end(&set->set);
 }
 
 int oidset_insert(struct oidset *set, const struct object_id *oid)
 {
-       struct oidmap_entry *entry;
-
-       if (!set->map.map.tablesize)
-               oidmap_init(&set->map, 0);
-       else if (oidset_contains(set, oid))
-               return 1;
-
-       entry = xmalloc(sizeof(*entry));
-       oidcpy(&entry->oid, oid);
-
-       oidmap_put(&set->map, entry);
-       return 0;
+       int added;
+       kh_put_oid(&set->set, *oid, &added);
+       return !added;
 }
 
 int oidset_remove(struct oidset *set, const struct object_id *oid)
 {
-       struct oidmap_entry *entry;
-
-       entry = oidmap_remove(&set->map, oid);
-       free(entry);
-
-       return (entry != NULL);
+       khiter_t pos = kh_get_oid(&set->set, *oid);
+       if (pos == kh_end(&set->set))
+               return 0;
+       kh_del_oid(&set->set, pos);
+       return 1;
 }
 
 void oidset_clear(struct oidset *set)
 {
-       oidmap_free(&set->map, 1);
+       kh_release_oid(&set->set);
+       oidset_init(set, 0);
 }
index 40ec5f87fe208e8e15feeb29ff0bb9d6325f44f8..c9d0f6d3cc8b99959d8637dcbf8ecb235021104e 100644 (file)
--- a/oidset.h
+++ b/oidset.h
@@ -1,7 +1,8 @@
 #ifndef OIDSET_H
 #define OIDSET_H
 
-#include "oidmap.h"
+#include "hashmap.h"
+#include "khash.h"
 
 /**
  * This API is similar to sha1-array, in that it maintains a set of object ids
  *      table overhead.
  */
 
+static inline unsigned int oid_hash(struct object_id oid)
+{
+       return sha1hash(oid.hash);
+}
+
+static inline int oid_equal(struct object_id a, struct object_id b)
+{
+       return oideq(&a, &b);
+}
+
+KHASH_INIT(oid, struct object_id, int, 0, oid_hash, oid_equal)
+
 /**
  * A single oidset; should be zero-initialized (or use OIDSET_INIT).
  */
 struct oidset {
-       struct oidmap map;
+       kh_oid_t set;
 };
 
-#define OIDSET_INIT { OIDMAP_INIT }
+#define OIDSET_INIT { { 0 } }
 
 
-static inline void oidset_init(struct oidset *set, size_t initial_size)
-{
-       oidmap_init(&set->map, initial_size);
-}
+/**
+ * Initialize the oidset structure `set`.
+ *
+ * If `initial_size` is bigger than 0 then preallocate to allow inserting
+ * the specified number of elements without further allocations.
+ */
+void oidset_init(struct oidset *set, size_t initial_size);
 
 /**
  * Returns true iff `set` contains `oid`.
@@ -58,19 +74,24 @@ int oidset_remove(struct oidset *set, const struct object_id *oid);
 void oidset_clear(struct oidset *set);
 
 struct oidset_iter {
-       struct oidmap_iter m_iter;
+       kh_oid_t *set;
+       khiter_t iter;
 };
 
 static inline void oidset_iter_init(struct oidset *set,
                                    struct oidset_iter *iter)
 {
-       oidmap_iter_init(&set->map, &iter->m_iter);
+       iter->set = &set->set;
+       iter->iter = kh_begin(iter->set);
 }
 
 static inline struct object_id *oidset_iter_next(struct oidset_iter *iter)
 {
-       struct oidmap_entry *e = oidmap_iter_next(&iter->m_iter);
-       return e ? &e->oid : NULL;
+       for (; iter->iter != kh_end(iter->set); iter->iter++) {
+               if (kh_exist(iter->set, iter->iter))
+                       return &kh_key(iter->set, iter->iter++);
+       }
+       return NULL;
 }
 
 static inline struct object_id *oidset_iter_first(struct oidset *set,
index fc82f37a02772244ee93aee41bef9238075c07ba..9d1b951697e9552b80a502fd587e43b615369635 100644 (file)
@@ -37,7 +37,7 @@ struct bitmap_writer {
 
        struct progress *progress;
        int show_progress;
-       unsigned char pack_checksum[20];
+       unsigned char pack_checksum[GIT_MAX_RAWSZ];
 };
 
 static struct bitmap_writer writer;
@@ -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);
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        revs.tag_objects = 1;
        revs.tree_objects = 1;
        revs.blob_objects = 1;
index 7e624c30ebd7f344d711b608b29b2d48d21a1ba0..b6cdbb0166fb5a3d6af3cfda2e7e259e3d54abe8 100644 (file)
@@ -148,6 +148,9 @@ 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
 }
 
 struct object_entry *packlist_alloc(struct packing_data *pdata,
index 2ca39cfcfe26aea164fc4173c49f0ffd05d4ef04..86ee93feb4f7d14518ed1ffe9cac8d8a7cd817fe 100644 (file)
@@ -377,7 +377,7 @@ static inline unsigned long oe_delta_size(struct packing_data *pack,
                return e->delta_size_;
 
        /*
-        * pack->detla_size[] can't be NULL because oe_set_delta_size()
+        * pack->delta_size[] can't be NULL because oe_set_delta_size()
         * must have been called when a new delta is saved with
         * oe_set_delta().
         * If oe_delta() returns NULL (i.e. default state, which means
index bb521cf7fb2911bc2d3653f46224880f8540f9ab..3c58784a5f4dedd0738ecc564f1945a10320c772 100644 (file)
@@ -122,13 +122,14 @@ static void create_pack_revindex(struct packed_git *p)
        unsigned num_ent = p->num_objects;
        unsigned i;
        const char *index = p->index_data;
+       const unsigned hashsz = the_hash_algo->rawsz;
 
        ALLOC_ARRAY(p->revindex, num_ent + 1);
        index += 4 * 256;
 
        if (p->index_version > 1) {
                const uint32_t *off_32 =
-                       (uint32_t *)(index + 8 + p->num_objects * (20 + 4));
+                       (uint32_t *)(index + 8 + p->num_objects * (hashsz + 4));
                const uint32_t *off_64 = off_32 + p->num_objects;
                for (i = 0; i < num_ent; i++) {
                        uint32_t off = ntohl(*off_32++);
@@ -142,16 +143,17 @@ static void create_pack_revindex(struct packed_git *p)
                }
        } else {
                for (i = 0; i < num_ent; i++) {
-                       uint32_t hl = *((uint32_t *)(index + 24 * i));
+                       uint32_t hl = *((uint32_t *)(index + (hashsz + 4) * i));
                        p->revindex[i].offset = ntohl(hl);
                        p->revindex[i].nr = i;
                }
        }
 
-       /* This knows the pack format -- the 20-byte trailer
+       /*
+        * This knows the pack format -- the hash trailer
         * follows immediately after the last object data.
         */
-       p->revindex[num_ent].offset = p->pack_size - 20;
+       p->revindex[num_ent].offset = p->pack_size - hashsz;
        p->revindex[num_ent].nr = -1;
        sort_revindex(p->revindex, num_ent, p->pack_size);
 }
index 841b36182fcd938da5ee0a4b065e0717ef4a18ed..f2850a00b58cccfec57335fb8e0500b7b1cba093 100644 (file)
@@ -80,10 +80,8 @@ void pack_report(void)
 static int check_packed_git_idx(const char *path, struct packed_git *p)
 {
        void *idx_map;
-       struct pack_idx_header *hdr;
        size_t idx_size;
-       uint32_t version, nr, i, *index;
-       int fd = git_open(path);
+       int fd = git_open(path), ret;
        struct stat st;
        const unsigned int hashsz = the_hash_algo->rawsz;
 
@@ -101,16 +99,32 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
        idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
 
-       hdr = idx_map;
+       ret = load_idx(path, hashsz, idx_map, idx_size, p);
+
+       if (ret)
+               munmap(idx_map, idx_size);
+
+       return ret;
+}
+
+int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
+            size_t idx_size, struct packed_git *p)
+{
+       struct pack_idx_header *hdr = idx_map;
+       uint32_t version, nr, i, *index;
+
+       if (idx_size < 4 * 256 + hashsz + hashsz)
+               return error("index file %s is too small", path);
+       if (idx_map == NULL)
+               return error("empty data");
+
        if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
                version = ntohl(hdr->idx_version);
-               if (version < 2 || version > 2) {
-                       munmap(idx_map, idx_size);
+               if (version < 2 || version > 2)
                        return error("index file %s is version %"PRIu32
                                     " and is not supported by this binary"
                                     " (try upgrading GIT to a newer version)",
                                     path, version);
-               }
        } else
                version = 1;
 
@@ -120,10 +134,8 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
                index += 2;  /* skip index header */
        for (i = 0; i < 256; i++) {
                uint32_t n = ntohl(index[i]);
-               if (n < nr) {
-                       munmap(idx_map, idx_size);
+               if (n < nr)
                        return error("non-monotonic index %s", path);
-               }
                nr = n;
        }
 
@@ -135,10 +147,8 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
                 *  - hash of the packfile
                 *  - file checksum
                 */
-               if (idx_size != 4*256 + nr * (hashsz + 4) + hashsz + hashsz) {
-                       munmap(idx_map, idx_size);
+               if (idx_size != 4 * 256 + nr * (hashsz + 4) + hashsz + hashsz)
                        return error("wrong index v1 file size in %s", path);
-               }
        } else if (version == 2) {
                /*
                 * Minimum size:
@@ -157,20 +167,16 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
                unsigned long max_size = min_size;
                if (nr)
                        max_size += (nr - 1)*8;
-               if (idx_size < min_size || idx_size > max_size) {
-                       munmap(idx_map, idx_size);
+               if (idx_size < min_size || idx_size > max_size)
                        return error("wrong index v2 file size in %s", path);
-               }
                if (idx_size != min_size &&
                    /*
                     * make sure we can deal with large pack offsets.
                     * 31-bit signed offset won't be enough, neither
                     * 32-bit unsigned one will be.
                     */
-                   (sizeof(off_t) <= 4)) {
-                       munmap(idx_map, idx_size);
+                   (sizeof(off_t) <= 4))
                        return error("pack too large for current definition of off_t in %s", path);
-               }
        }
 
        p->index_version = version;
@@ -1121,13 +1127,14 @@ int unpack_object_header(struct packed_git *p,
 void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1)
 {
        unsigned i;
+       const unsigned hashsz = the_hash_algo->rawsz;
        for (i = 0; i < p->num_bad_objects; i++)
-               if (hasheq(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
+               if (hasheq(sha1, p->bad_object_sha1 + hashsz * i))
                        return;
        p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
                                      st_mult(GIT_MAX_RAWSZ,
                                              st_add(p->num_bad_objects, 1)));
-       hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
+       hashcpy(p->bad_object_sha1 + hashsz * p->num_bad_objects, sha1);
        p->num_bad_objects++;
 }
 
index 442625723dea4b0c8f22b57d52e760d4b810540e..6c4037605d0dfee59a084c440506f1af11708d63 100644 (file)
@@ -164,4 +164,17 @@ extern int has_pack_index(const unsigned char *sha1);
  */
 extern int is_promisor_object(const struct object_id *oid);
 
+/*
+ * Expose a function for fuzz testing.
+ *
+ * load_idx() parses a block of memory as a packfile index and puts the results
+ * into a struct packed_git.
+ *
+ * This function should not be used directly. It is exposed here only so that we
+ * have a convenient entry-point for fuzz testing. For real uses, you should
+ * probably use open_pack_index() or parse_pack_index() instead.
+ */
+extern int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
+                   size_t idx_size, struct packed_git *p);
+
 #endif
index 960ea2405412cc5100e1bda4cb179248d04284df..c262e1be9c9c912cc12c5b60a08e69f8cf9a30c9 100644 (file)
@@ -56,10 +56,10 @@ static int patch_id_neq(const void *cmpfn_data,
        return !oideq(&a->patch_id, &b->patch_id);
 }
 
-int init_patch_ids(struct patch_ids *ids)
+int init_patch_ids(struct repository *r, struct patch_ids *ids)
 {
        memset(ids, 0, sizeof(*ids));
-       diff_setup(&ids->diffopts);
+       repo_diff_setup(r, &ids->diffopts);
        ids->diffopts.detect_rename = 0;
        ids->diffopts.flags.recursive = 1;
        diff_setup_done(&ids->diffopts);
index 79ac9a8498383b971107f4b30a7a75dbd30613d1..82a12b66f8891e5a9a45c2a63845dd28de7eeda7 100644 (file)
@@ -6,6 +6,7 @@
 
 struct commit;
 struct object_id;
+struct repository;
 
 struct patch_id {
        struct hashmap_entry ent;
@@ -20,7 +21,7 @@ struct patch_ids {
 
 int commit_patch_id(struct commit *commit, struct diff_options *options,
                    struct object_id *oid, int);
-int init_patch_ids(struct patch_ids *);
+int init_patch_ids(struct repository *, struct patch_ids *);
 int free_patch_ids(struct patch_ids *);
 struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);
 struct patch_id *has_commit_patch_id(struct commit *, struct patch_ids *);
index f7365761f4725d6f7ddbb6d8c9c8fe92488de6ce..9e7152ab14d9359d0a48da8b25cd58253a13fc0c 100644 (file)
@@ -5,10 +5,13 @@
 #include "pathspec.h"
 #include "dir.h"
 #include "fsmonitor.h"
+#include "config.h"
+#include "progress.h"
 
 #ifdef NO_PTHREADS
 static void preload_index(struct index_state *index,
-                         const struct pathspec *pathspec)
+                         const struct pathspec *pathspec,
+                         unsigned int refresh_flags)
 {
        ; /* nothing */
 }
@@ -25,16 +28,23 @@ static void preload_index(struct index_state *index,
 #define MAX_PARALLEL (20)
 #define THREAD_COST (500)
 
+struct progress_data {
+       unsigned long n;
+       struct progress *progress;
+       pthread_mutex_t mutex;
+};
+
 struct thread_data {
        pthread_t pthread;
        struct index_state *index;
        struct pathspec pathspec;
+       struct progress_data *progress;
        int offset, nr;
 };
 
 static void *preload_thread(void *_data)
 {
-       int nr;
+       int nr, last_nr;
        struct thread_data *p = _data;
        struct index_state *index = p->index;
        struct cache_entry **cep = index->cache + p->offset;
@@ -43,6 +53,7 @@ static void *preload_thread(void *_data)
        nr = p->nr;
        if (nr + p->offset > index->cache_nr)
                nr = index->cache_nr - p->offset;
+       last_nr = nr;
 
        do {
                struct cache_entry *ce = *cep++;
@@ -58,6 +69,15 @@ static void *preload_thread(void *_data)
                        continue;
                if (ce->ce_flags & CE_FSMONITOR_VALID)
                        continue;
+               if (p->progress && !(nr & 31)) {
+                       struct progress_data *pd = p->progress;
+
+                       pthread_mutex_lock(&pd->mutex);
+                       pd->n += last_nr - nr;
+                       display_progress(pd->progress, pd->n);
+                       pthread_mutex_unlock(&pd->mutex);
+                       last_nr = nr;
+               }
                if (!ce_path_match(index, ce, &p->pathspec, NULL))
                        continue;
                if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
@@ -69,21 +89,30 @@ static void *preload_thread(void *_data)
                ce_mark_uptodate(ce);
                mark_fsmonitor_valid(ce);
        } while (--nr > 0);
+       if (p->progress) {
+               struct progress_data *pd = p->progress;
+
+               pthread_mutex_lock(&pd->mutex);
+               display_progress(pd->progress, pd->n + last_nr);
+               pthread_mutex_unlock(&pd->mutex);
+       }
        cache_def_clear(&cache);
        return NULL;
 }
 
 static void preload_index(struct index_state *index,
-                         const struct pathspec *pathspec)
+                         const struct pathspec *pathspec,
+                         unsigned int refresh_flags)
 {
        int threads, i, work, offset;
        struct thread_data data[MAX_PARALLEL];
+       struct progress_data pd;
 
        if (!core_preload_index)
                return;
 
        threads = index->cache_nr / THREAD_COST;
-       if ((index->cache_nr > 1) && (threads < 2) && getenv("GIT_FORCE_PRELOAD_TEST"))
+       if ((index->cache_nr > 1) && (threads < 2) && git_env_bool("GIT_TEST_PRELOAD_INDEX", 0))
                threads = 2;
        if (threads < 2)
                return;
@@ -93,6 +122,13 @@ static void preload_index(struct index_state *index,
        offset = 0;
        work = DIV_ROUND_UP(index->cache_nr, threads);
        memset(&data, 0, sizeof(data));
+
+       memset(&pd, 0, sizeof(pd));
+       if (refresh_flags & REFRESH_PROGRESS && isatty(2)) {
+               pd.progress = start_delayed_progress(_("Refreshing index"), index->cache_nr);
+               pthread_mutex_init(&pd.mutex, NULL);
+       }
+
        for (i = 0; i < threads; i++) {
                struct thread_data *p = data+i;
                p->index = index;
@@ -100,6 +136,8 @@ static void preload_index(struct index_state *index,
                        copy_pathspec(&p->pathspec, pathspec);
                p->offset = offset;
                p->nr = work;
+               if (pd.progress)
+                       p->progress = &pd;
                offset += work;
                if (pthread_create(&p->pthread, NULL, preload_thread, p))
                        die("unable to create threaded lstat");
@@ -109,15 +147,18 @@ static void preload_index(struct index_state *index,
                if (pthread_join(p->pthread, NULL))
                        die("unable to join threaded lstat");
        }
+       stop_progress(&pd.progress);
+
        trace_performance_leave("preload index");
 }
 #endif
 
 int read_index_preload(struct index_state *index,
-                      const struct pathspec *pathspec)
+                      const struct pathspec *pathspec,
+                      unsigned int refresh_flags)
 {
        int retval = read_index(index);
 
-       preload_index(index, pathspec);
+       preload_index(index, pathspec, refresh_flags);
        return retval;
 }
index 8ca29e92815608437ccb1fb9909e78d5b4989529..b83a3ecd2331af7f2e7e03bd76336cde3c2a6dfc 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1256,6 +1256,14 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        if (c->signature_check.key)
                                strbuf_addstr(sb, c->signature_check.key);
                        break;
+               case 'F':
+                       if (c->signature_check.fingerprint)
+                               strbuf_addstr(sb, c->signature_check.fingerprint);
+                       break;
+               case 'P':
+                       if (c->signature_check.primary_key_fingerprint)
+                               strbuf_addstr(sb, c->signature_check.primary_key_fingerprint);
+                       break;
                default:
                        return 0;
                }
index 60edb2f518d6d696b8091ef6246791cd730f326f..bd8083f2d10cea9a464ebfd1490295d13267421a 100644 (file)
@@ -354,7 +354,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p)
 {
        struct diff_filespec *spec = alloc_filespec(name);
 
-       fill_filespec(spec, &null_oid, 0, 0644);
+       fill_filespec(spec, &null_oid, 0, 0100644);
        spec->data = (char *)p;
        spec->size = strlen(p);
        spec->should_munmap = 0;
index 8d04d78a5877aab74dd35e24a31bcfe3e0a3417d..d57958233e82df6551fdc890f1238ae65beb8940 100644 (file)
@@ -23,6 +23,8 @@
 #include "split-index.h"
 #include "utf8.h"
 #include "fsmonitor.h"
+#include "thread-utils.h"
+#include "progress.h"
 
 /* Mask for the name length in ce_flags in the on-disk index */
 
@@ -43,6 +45,8 @@
 #define CACHE_EXT_LINK 0x6c696e6b        /* "link" */
 #define CACHE_EXT_UNTRACKED 0x554E5452   /* "UNTR" */
 #define CACHE_EXT_FSMONITOR 0x46534D4E   /* "FSMN" */
+#define CACHE_EXT_ENDOFINDEXENTRIES 0x454F4945 /* "EOIE" */
+#define CACHE_EXT_INDEXENTRYOFFSETTABLE 0x49454F54 /* "IEOT" */
 
 /* changes that can be kept in $GIT_DIR/index (basically all extensions) */
 #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
@@ -205,14 +209,16 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
        }
 }
 
-static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
+static int ce_compare_data(struct index_state *istate,
+                          const struct cache_entry *ce,
+                          struct stat *st)
 {
        int match = -1;
        int fd = git_open_cloexec(ce->name, O_RDONLY);
 
        if (fd >= 0) {
                struct object_id oid;
-               if (!index_fd(&oid, fd, st, OBJ_BLOB, ce->name, 0))
+               if (!index_fd(istate, &oid, fd, st, OBJ_BLOB, ce->name, 0))
                        match = !oideq(&oid, &ce->oid);
                /* index_fd() closed the file descriptor already */
        }
@@ -257,11 +263,13 @@ static int ce_compare_gitlink(const struct cache_entry *ce)
        return !oideq(&oid, &ce->oid);
 }
 
-static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
+static int ce_modified_check_fs(struct index_state *istate,
+                               const struct cache_entry *ce,
+                               struct stat *st)
 {
        switch (st->st_mode & S_IFMT) {
        case S_IFREG:
-               if (ce_compare_data(ce, st))
+               if (ce_compare_data(istate, ce, st))
                        return DATA_CHANGED;
                break;
        case S_IFLNK:
@@ -337,7 +345,7 @@ static int is_racy_stat(const struct index_state *istate,
                );
 }
 
-static int is_racy_timestamp(const struct index_state *istate,
+int is_racy_timestamp(const struct index_state *istate,
                             const struct cache_entry *ce)
 {
        return (!S_ISGITLINK(ce->ce_mode) &&
@@ -407,7 +415,7 @@ int ie_match_stat(struct index_state *istate,
                if (assume_racy_is_modified)
                        changed |= DATA_CHANGED;
                else
-                       changed |= ce_modified_check_fs(ce, st);
+                       changed |= ce_modified_check_fs(istate, ce, st);
        }
 
        return changed;
@@ -447,7 +455,7 @@ int ie_modified(struct index_state *istate,
            (S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
                return changed;
 
-       changed_fs = ce_modified_check_fs(ce, st);
+       changed_fs = ce_modified_check_fs(istate, ce, st);
        if (changed_fs)
                return changed | changed_fs;
        return 0;
@@ -753,7 +761,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                }
        }
        if (!intent_only) {
-               if (index_path(&ce->oid, path, st, newflags)) {
+               if (index_path(istate, &ce->oid, path, st, newflags)) {
                        discard_cache_entry(ce);
                        return error("unable to index file %s", path);
                }
@@ -823,7 +831,7 @@ struct cache_entry *make_cache_entry(struct index_state *istate,
        ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
 
-       ret = refresh_cache_entry(&the_index, ce, refresh_options);
+       ret = refresh_cache_entry(istate, ce, refresh_options);
        if (ret != ce)
                discard_cache_entry(ce);
        return ret;
@@ -1476,6 +1484,11 @@ int refresh_index(struct index_state *istate, unsigned int flags,
        const char *typechange_fmt;
        const char *added_fmt;
        const char *unmerged_fmt;
+       struct progress *progress = NULL;
+
+       if (flags & REFRESH_PROGRESS && isatty(2))
+               progress = start_delayed_progress(_("Refresh index"),
+                                                 istate->cache_nr);
 
        trace_performance_enter();
        modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
@@ -1493,7 +1506,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
                        continue;
 
-               if (pathspec && !ce_path_match(&the_index, ce, pathspec, seen))
+               if (pathspec && !ce_path_match(istate, ce, pathspec, seen))
                        filtered = 1;
 
                if (ce_stage(ce)) {
@@ -1516,6 +1529,8 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                new_entry = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
                if (new_entry == ce)
                        continue;
+               if (progress)
+                       display_progress(progress, i);
                if (!new_entry) {
                        const char *fmt;
 
@@ -1547,6 +1562,10 @@ int refresh_index(struct index_state *istate, unsigned int flags,
 
                replace_index_entry(istate, i, new_entry);
        }
+       if (progress) {
+               display_progress(progress, istate->cache_nr);
+               stop_progress(&progress);
+       }
        trace_performance_leave("refresh index");
        return has_errors;
 }
@@ -1650,7 +1669,7 @@ int verify_index_checksum;
 /* Allow fsck to force verification of the cache entry order. */
 int verify_ce_order;
 
-static int verify_hdr(struct cache_header *hdr, unsigned long size)
+static int verify_hdr(const struct cache_header *hdr, unsigned long size)
 {
        git_hash_ctx c;
        unsigned char hash[GIT_MAX_RAWSZ];
@@ -1674,7 +1693,7 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
 }
 
 static int read_index_extension(struct index_state *istate,
-                               const char *ext, void *data, unsigned long sz)
+                               const char *ext, const char *data, unsigned long sz)
 {
        switch (CACHE_EXT(ext)) {
        case CACHE_EXT_TREE:
@@ -1693,6 +1712,10 @@ static int read_index_extension(struct index_state *istate,
        case CACHE_EXT_FSMONITOR:
                read_fsmonitor_extension(istate, data, sz);
                break;
+       case CACHE_EXT_ENDOFINDEXENTRIES:
+       case CACHE_EXT_INDEXENTRYOFFSETTABLE:
+               /* already handled in do_read_index() */
+               break;
        default:
                if (*ext < 'A' || 'Z' < *ext)
                        return error("index uses %.4s extension, which we do not understand",
@@ -1713,63 +1736,25 @@ int read_index(struct index_state *istate)
        return read_index_from(istate, get_index_file(), get_git_dir());
 }
 
-static struct cache_entry *cache_entry_from_ondisk(struct mem_pool *mem_pool,
-                                                  struct ondisk_cache_entry *ondisk,
-                                                  unsigned int flags,
-                                                  const char *name,
-                                                  size_t len)
-{
-       struct cache_entry *ce = mem_pool__ce_alloc(mem_pool, len);
-
-       ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
-       ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
-       ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
-       ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
-       ce->ce_stat_data.sd_dev   = get_be32(&ondisk->dev);
-       ce->ce_stat_data.sd_ino   = get_be32(&ondisk->ino);
-       ce->ce_mode  = get_be32(&ondisk->mode);
-       ce->ce_stat_data.sd_uid   = get_be32(&ondisk->uid);
-       ce->ce_stat_data.sd_gid   = get_be32(&ondisk->gid);
-       ce->ce_stat_data.sd_size  = get_be32(&ondisk->size);
-       ce->ce_flags = flags & ~CE_NAMEMASK;
-       ce->ce_namelen = len;
-       ce->index = 0;
-       hashcpy(ce->oid.hash, ondisk->sha1);
-       memcpy(ce->name, name, len);
-       ce->name[len] = '\0';
-       return ce;
-}
-
-/*
- * Adjacent cache entries tend to share the leading paths, so it makes
- * sense to only store the differences in later entries.  In the v4
- * on-disk format of the index, each on-disk cache entry stores the
- * number of bytes to be stripped from the end of the previous name,
- * and the bytes to append to the result, to come up with its name.
- */
-static unsigned long expand_name_field(struct strbuf *name, const char *cp_)
-{
-       const unsigned char *ep, *cp = (const unsigned char *)cp_;
-       size_t len = decode_varint(&cp);
-
-       if (name->len < len)
-               die("malformed name field in the index");
-       strbuf_remove(name, name->len - len, len);
-       for (ep = cp; *ep; ep++)
-               ; /* find the end */
-       strbuf_add(name, cp, ep - cp);
-       return (const char *)ep + 1 - cp_;
-}
-
-static struct cache_entry *create_from_disk(struct mem_pool *mem_pool,
+static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
+                                           unsigned int version,
                                            struct ondisk_cache_entry *ondisk,
                                            unsigned long *ent_size,
-                                           struct strbuf *previous_name)
+                                           const struct cache_entry *previous_ce)
 {
        struct cache_entry *ce;
        size_t len;
        const char *name;
        unsigned int flags;
+       size_t copy_len;
+       /*
+        * Adjacent cache entries tend to share the leading paths, so it makes
+        * sense to only store the differences in later entries.  In the v4
+        * on-disk format of the index, each on-disk cache entry stores the
+        * number of bytes to be stripped from the end of the previous name,
+        * and the bytes to append to the result, to come up with its name.
+        */
+       int expand_name_field = version == 4;
 
        /* On-disk flags are just 16 bits */
        flags = get_be16(&ondisk->flags);
@@ -1789,21 +1774,55 @@ static struct cache_entry *create_from_disk(struct mem_pool *mem_pool,
        else
                name = ondisk->name;
 
-       if (!previous_name) {
-               /* v3 and earlier */
-               if (len == CE_NAMEMASK)
-                       len = strlen(name);
-               ce = cache_entry_from_ondisk(mem_pool, ondisk, flags, name, len);
+       if (expand_name_field) {
+               const unsigned char *cp = (const unsigned char *)name;
+               size_t strip_len, previous_len;
+
+               /* If we're at the begining of a block, ignore the previous name */
+               strip_len = decode_varint(&cp);
+               if (previous_ce) {
+                       previous_len = previous_ce->ce_namelen;
+                       if (previous_len < strip_len)
+                               die(_("malformed name field in the index, near path '%s'"),
+                                       previous_ce->name);
+                       copy_len = previous_len - strip_len;
+               } else {
+                       copy_len = 0;
+               }
+               name = (const char *)cp;
+       }
 
-               *ent_size = ondisk_ce_size(ce);
-       } else {
-               unsigned long consumed;
-               consumed = expand_name_field(previous_name, name);
-               ce = cache_entry_from_ondisk(mem_pool, ondisk, flags,
-                                            previous_name->buf,
-                                            previous_name->len);
+       if (len == CE_NAMEMASK) {
+               len = strlen(name);
+               if (expand_name_field)
+                       len += copy_len;
+       }
+
+       ce = mem_pool__ce_alloc(ce_mem_pool, len);
+
+       ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
+       ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
+       ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
+       ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
+       ce->ce_stat_data.sd_dev   = get_be32(&ondisk->dev);
+       ce->ce_stat_data.sd_ino   = get_be32(&ondisk->ino);
+       ce->ce_mode  = get_be32(&ondisk->mode);
+       ce->ce_stat_data.sd_uid   = get_be32(&ondisk->uid);
+       ce->ce_stat_data.sd_gid   = get_be32(&ondisk->gid);
+       ce->ce_stat_data.sd_size  = get_be32(&ondisk->size);
+       ce->ce_flags = flags & ~CE_NAMEMASK;
+       ce->ce_namelen = len;
+       ce->index = 0;
+       hashcpy(ce->oid.hash, ondisk->sha1);
 
-               *ent_size = (name - ((char *)ondisk)) + consumed;
+       if (expand_name_field) {
+               if (copy_len)
+                       memcpy(ce->name, previous_ce->name, copy_len);
+               memcpy(ce->name + copy_len, name, len + 1 - copy_len);
+               *ent_size = (name - ((char *)ondisk)) + len + 1 - copy_len;
+       } else {
+               memcpy(ce->name, name, len + 1);
+               *ent_size = ondisk_ce_size(ce);
        }
        return ce;
 }
@@ -1889,16 +1908,237 @@ static size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
        return ondisk_size + entries * per_entry;
 }
 
+struct index_entry_offset
+{
+       /* starting byte offset into index file, count of index entries in this block */
+       int offset, nr;
+};
+
+struct index_entry_offset_table
+{
+       int nr;
+       struct index_entry_offset entries[FLEX_ARRAY];
+};
+
+#ifndef NO_PTHREADS
+static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset);
+static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot);
+#endif
+
+static size_t read_eoie_extension(const char *mmap, size_t mmap_size);
+static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset);
+
+struct load_index_extensions
+{
+#ifndef NO_PTHREADS
+       pthread_t pthread;
+#endif
+       struct index_state *istate;
+       const char *mmap;
+       size_t mmap_size;
+       unsigned long src_offset;
+};
+
+static void *load_index_extensions(void *_data)
+{
+       struct load_index_extensions *p = _data;
+       unsigned long src_offset = p->src_offset;
+
+       while (src_offset <= p->mmap_size - the_hash_algo->rawsz - 8) {
+               /* After an array of active_nr index entries,
+                * there can be arbitrary number of extended
+                * sections, each of which is prefixed with
+                * extension name (4-byte) and section length
+                * in 4-byte network byte order.
+                */
+               uint32_t extsize = get_be32(p->mmap + src_offset + 4);
+               if (read_index_extension(p->istate,
+                                        p->mmap + src_offset,
+                                        p->mmap + src_offset + 8,
+                                        extsize) < 0) {
+                       munmap((void *)p->mmap, p->mmap_size);
+                       die(_("index file corrupt"));
+               }
+               src_offset += 8;
+               src_offset += extsize;
+       }
+
+       return NULL;
+}
+
+/*
+ * A helper function that will load the specified range of cache entries
+ * from the memory mapped file and add them to the given index.
+ */
+static unsigned long load_cache_entry_block(struct index_state *istate,
+                       struct mem_pool *ce_mem_pool, int offset, int nr, const char *mmap,
+                       unsigned long start_offset, const struct cache_entry *previous_ce)
+{
+       int i;
+       unsigned long src_offset = start_offset;
+
+       for (i = offset; i < offset + nr; i++) {
+               struct ondisk_cache_entry *disk_ce;
+               struct cache_entry *ce;
+               unsigned long consumed;
+
+               disk_ce = (struct ondisk_cache_entry *)(mmap + src_offset);
+               ce = create_from_disk(ce_mem_pool, istate->version, disk_ce, &consumed, previous_ce);
+               set_index_entry(istate, i, ce);
+
+               src_offset += consumed;
+               previous_ce = ce;
+       }
+       return src_offset - start_offset;
+}
+
+static unsigned long load_all_cache_entries(struct index_state *istate,
+                       const char *mmap, size_t mmap_size, unsigned long src_offset)
+{
+       unsigned long consumed;
+
+       if (istate->version == 4) {
+               mem_pool_init(&istate->ce_mem_pool,
+                               estimate_cache_size_from_compressed(istate->cache_nr));
+       } else {
+               mem_pool_init(&istate->ce_mem_pool,
+                               estimate_cache_size(mmap_size, istate->cache_nr));
+       }
+
+       consumed = load_cache_entry_block(istate, istate->ce_mem_pool,
+                                       0, istate->cache_nr, mmap, src_offset, NULL);
+       return consumed;
+}
+
+#ifndef NO_PTHREADS
+
+/*
+ * Mostly randomly chosen maximum thread counts: we
+ * cap the parallelism to online_cpus() threads, and we want
+ * to have at least 10000 cache entries per thread for it to
+ * be worth starting a thread.
+ */
+
+#define THREAD_COST            (10000)
+
+struct load_cache_entries_thread_data
+{
+       pthread_t pthread;
+       struct index_state *istate;
+       struct mem_pool *ce_mem_pool;
+       int offset;
+       const char *mmap;
+       struct index_entry_offset_table *ieot;
+       int ieot_start;         /* starting index into the ieot array */
+       int ieot_blocks;        /* count of ieot entries to process */
+       unsigned long consumed; /* return # of bytes in index file processed */
+};
+
+/*
+ * A thread proc to run the load_cache_entries() computation
+ * across multiple background threads.
+ */
+static void *load_cache_entries_thread(void *_data)
+{
+       struct load_cache_entries_thread_data *p = _data;
+       int i;
+
+       /* iterate across all ieot blocks assigned to this thread */
+       for (i = p->ieot_start; i < p->ieot_start + p->ieot_blocks; i++) {
+               p->consumed += load_cache_entry_block(p->istate, p->ce_mem_pool,
+                       p->offset, p->ieot->entries[i].nr, p->mmap, p->ieot->entries[i].offset, NULL);
+               p->offset += p->ieot->entries[i].nr;
+       }
+       return NULL;
+}
+
+static unsigned long load_cache_entries_threaded(struct index_state *istate, const char *mmap, size_t mmap_size,
+                       unsigned long src_offset, int nr_threads, struct index_entry_offset_table *ieot)
+{
+       int i, offset, ieot_blocks, ieot_start, err;
+       struct load_cache_entries_thread_data *data;
+       unsigned long consumed = 0;
+
+       /* a little sanity checking */
+       if (istate->name_hash_initialized)
+               BUG("the name hash isn't thread safe");
+
+       mem_pool_init(&istate->ce_mem_pool, 0);
+
+       /* ensure we have no more threads than we have blocks to process */
+       if (nr_threads > ieot->nr)
+               nr_threads = ieot->nr;
+       data = xcalloc(nr_threads, sizeof(*data));
+
+       offset = ieot_start = 0;
+       ieot_blocks = DIV_ROUND_UP(ieot->nr, nr_threads);
+       for (i = 0; i < nr_threads; i++) {
+               struct load_cache_entries_thread_data *p = &data[i];
+               int nr, j;
+
+               if (ieot_start + ieot_blocks > ieot->nr)
+                       ieot_blocks = ieot->nr - ieot_start;
+
+               p->istate = istate;
+               p->offset = offset;
+               p->mmap = mmap;
+               p->ieot = ieot;
+               p->ieot_start = ieot_start;
+               p->ieot_blocks = ieot_blocks;
+
+               /* create a mem_pool for each thread */
+               nr = 0;
+               for (j = p->ieot_start; j < p->ieot_start + p->ieot_blocks; j++)
+                       nr += p->ieot->entries[j].nr;
+               if (istate->version == 4) {
+                       mem_pool_init(&p->ce_mem_pool,
+                               estimate_cache_size_from_compressed(nr));
+               } else {
+                       mem_pool_init(&p->ce_mem_pool,
+                               estimate_cache_size(mmap_size, nr));
+               }
+
+               err = pthread_create(&p->pthread, NULL, load_cache_entries_thread, p);
+               if (err)
+                       die(_("unable to create load_cache_entries thread: %s"), strerror(err));
+
+               /* increment by the number of cache entries in the ieot block being processed */
+               for (j = 0; j < ieot_blocks; j++)
+                       offset += ieot->entries[ieot_start + j].nr;
+               ieot_start += ieot_blocks;
+       }
+
+       for (i = 0; i < nr_threads; i++) {
+               struct load_cache_entries_thread_data *p = &data[i];
+
+               err = pthread_join(p->pthread, NULL);
+               if (err)
+                       die(_("unable to join load_cache_entries thread: %s"), strerror(err));
+               mem_pool_combine(istate->ce_mem_pool, p->ce_mem_pool);
+               consumed += p->consumed;
+       }
+
+       free(data);
+
+       return consumed;
+}
+#endif
+
 /* remember to discard_cache() before reading a different cache! */
 int do_read_index(struct index_state *istate, const char *path, int must_exist)
 {
-       int fd, i;
+       int fd;
        struct stat st;
        unsigned long src_offset;
-       struct cache_header *hdr;
-       void *mmap;
+       const struct cache_header *hdr;
+       const char *mmap;
        size_t mmap_size;
-       struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
+       struct load_index_extensions p;
+       size_t extension_offset = 0;
+#ifndef NO_PTHREADS
+       int nr_threads, cpus;
+       struct index_entry_offset_table *ieot = NULL;
+#endif
 
        if (istate->initialized)
                return istate->cache_nr;
@@ -1924,7 +2164,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
                die_errno("unable to map index file");
        close(fd);
 
-       hdr = mmap;
+       hdr = (const struct cache_header *)mmap;
        if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
 
@@ -1935,55 +2175,74 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
        istate->cache = xcalloc(istate->cache_alloc, sizeof(*istate->cache));
        istate->initialized = 1;
 
-       if (istate->version == 4) {
-               previous_name = &previous_name_buf;
-               mem_pool_init(&istate->ce_mem_pool,
-                             estimate_cache_size_from_compressed(istate->cache_nr));
-       } else {
-               previous_name = NULL;
-               mem_pool_init(&istate->ce_mem_pool,
-                             estimate_cache_size(mmap_size, istate->cache_nr));
-       }
+       p.istate = istate;
+       p.mmap = mmap;
+       p.mmap_size = mmap_size;
 
        src_offset = sizeof(*hdr);
-       for (i = 0; i < istate->cache_nr; i++) {
-               struct ondisk_cache_entry *disk_ce;
-               struct cache_entry *ce;
-               unsigned long consumed;
 
-               disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset);
-               ce = create_from_disk(istate->ce_mem_pool, disk_ce, &consumed, previous_name);
-               set_index_entry(istate, i, ce);
+#ifndef NO_PTHREADS
+       nr_threads = git_config_get_index_threads();
 
-               src_offset += consumed;
+       /* TODO: does creating more threads than cores help? */
+       if (!nr_threads) {
+               nr_threads = istate->cache_nr / THREAD_COST;
+               cpus = online_cpus();
+               if (nr_threads > cpus)
+                       nr_threads = cpus;
        }
-       strbuf_release(&previous_name_buf);
+
+       if (nr_threads > 1) {
+               extension_offset = read_eoie_extension(mmap, mmap_size);
+               if (extension_offset) {
+                       int err;
+
+                       p.src_offset = extension_offset;
+                       err = pthread_create(&p.pthread, NULL, load_index_extensions, &p);
+                       if (err)
+                               die(_("unable to create load_index_extensions thread: %s"), strerror(err));
+
+                       nr_threads--;
+               }
+       }
+
+       /*
+        * Locate and read the index entry offset table so that we can use it
+        * to multi-thread the reading of the cache entries.
+        */
+       if (extension_offset && nr_threads > 1)
+               ieot = read_ieot_extension(mmap, mmap_size, extension_offset);
+
+       if (ieot) {
+               src_offset += load_cache_entries_threaded(istate, mmap, mmap_size, src_offset, nr_threads, ieot);
+               free(ieot);
+       } else {
+               src_offset += load_all_cache_entries(istate, mmap, mmap_size, src_offset);
+       }
+#else
+       src_offset += load_all_cache_entries(istate, mmap, mmap_size, src_offset);
+#endif
+
        istate->timestamp.sec = st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
 
-       while (src_offset <= mmap_size - the_hash_algo->rawsz - 8) {
-               /* After an array of active_nr index entries,
-                * there can be arbitrary number of extended
-                * sections, each of which is prefixed with
-                * extension name (4-byte) and section length
-                * in 4-byte network byte order.
-                */
-               uint32_t extsize;
-               memcpy(&extsize, (char *)mmap + src_offset + 4, 4);
-               extsize = ntohl(extsize);
-               if (read_index_extension(istate,
-                                        (const char *) mmap + src_offset,
-                                        (char *) mmap + src_offset + 8,
-                                        extsize) < 0)
-                       goto unmap;
-               src_offset += 8;
-               src_offset += extsize;
+       /* if we created a thread, join it otherwise load the extensions on the primary thread */
+#ifndef NO_PTHREADS
+       if (extension_offset) {
+               int ret = pthread_join(p.pthread, NULL);
+               if (ret)
+                       die(_("unable to join load_index_extensions thread: %s"), strerror(ret));
        }
-       munmap(mmap, mmap_size);
+#endif
+       if (!extension_offset) {
+               p.src_offset = src_offset;
+               load_index_extensions(&p);
+       }
+       munmap((void *)mmap, mmap_size);
        return istate->cache_nr;
 
 unmap:
-       munmap(mmap, mmap_size);
+       munmap((void *)mmap, mmap_size);
        die("index file corrupt");
 }
 
@@ -2123,7 +2382,7 @@ int unmerged_index(const struct index_state *istate)
        return 0;
 }
 
-int index_has_changes(const struct index_state *istate,
+int index_has_changes(struct index_state *istate,
                      struct tree *tree,
                      struct strbuf *sb)
 {
@@ -2138,7 +2397,7 @@ int index_has_changes(const struct index_state *istate,
        if (tree || !get_oid_tree("HEAD", &cmp)) {
                struct diff_options opt;
 
-               diff_setup(&opt);
+               repo_diff_setup(the_repository, &opt);
                opt.flags.exit_with_status = 1;
                if (!sb)
                        opt.flags.quick = 1;
@@ -2199,11 +2458,15 @@ static int ce_write(git_hash_ctx *context, int fd, void *data, unsigned int len)
        return 0;
 }
 
-static int write_index_ext_header(git_hash_ctx *context, int fd,
-                                 unsigned int ext, unsigned int sz)
+static int write_index_ext_header(git_hash_ctx *context, git_hash_ctx *eoie_context,
+                                 int fd, unsigned int ext, unsigned int sz)
 {
        ext = htonl(ext);
        sz = htonl(sz);
+       if (eoie_context) {
+               the_hash_algo->update_fn(eoie_context, &ext, 4);
+               the_hash_algo->update_fn(eoie_context, &sz, 4);
+       }
        return ((ce_write(context, fd, &ext, 4) < 0) ||
                (ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
 }
@@ -2231,7 +2494,8 @@ static int ce_flush(git_hash_ctx *context, int fd, unsigned char *hash)
        return (write_in_full(fd, write_buffer, left) < 0) ? -1 : 0;
 }
 
-static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
+static void ce_smudge_racily_clean_entry(struct index_state *istate,
+                                        struct cache_entry *ce)
 {
        /*
         * The only thing we care about in this function is to smudge the
@@ -2250,7 +2514,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
                return;
        if (ce_match_stat_basic(ce, &st))
                return;
-       if (ce_modified_check_fs(ce, &st)) {
+       if (ce_modified_check_fs(istate, ce, &st)) {
                /* This is "racily clean"; smudge it.  Note that this
                 * is a tricky code.  At first glance, it may appear
                 * that it can break with this sequence:
@@ -2446,7 +2710,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
 {
        uint64_t start = getnanotime();
        int newfd = tempfile->fd;
-       git_hash_ctx c;
+       git_hash_ctx c, eoie_c;
        struct cache_header hdr;
        int i, err = 0, removed, extended, hdr_version;
        struct cache_entry **cache = istate->cache;
@@ -2455,6 +2719,10 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        struct ondisk_cache_entry_extended ondisk;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
        int drop_cache_tree = istate->drop_cache_tree;
+       off_t offset;
+       int ieot_entries = 1;
+       struct index_entry_offset_table *ieot = NULL;
+       int nr, nr_threads;
 
        for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
@@ -2488,6 +2756,46 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
                return -1;
 
+#ifndef NO_PTHREADS
+       nr_threads = git_config_get_index_threads();
+       if (nr_threads != 1) {
+               int ieot_blocks, cpus;
+
+               /*
+                * ensure default number of ieot blocks maps evenly to the
+                * default number of threads that will process them leaving
+                * room for the thread to load the index extensions.
+                */
+               if (!nr_threads) {
+                       ieot_blocks = istate->cache_nr / THREAD_COST;
+                       cpus = online_cpus();
+                       if (ieot_blocks > cpus - 1)
+                               ieot_blocks = cpus - 1;
+               } else {
+                       ieot_blocks = nr_threads;
+                       if (ieot_blocks > istate->cache_nr)
+                               ieot_blocks = istate->cache_nr;
+               }
+
+               /*
+                * no reason to write out the IEOT extension if we don't
+                * have enough blocks to utilize multi-threading
+                */
+               if (ieot_blocks > 1) {
+                       ieot = xcalloc(1, sizeof(struct index_entry_offset_table)
+                               + (ieot_blocks * sizeof(struct index_entry_offset)));
+                       ieot_entries = DIV_ROUND_UP(entries, ieot_blocks);
+               }
+       }
+#endif
+
+       offset = lseek(newfd, 0, SEEK_CUR);
+       if (offset < 0) {
+               free(ieot);
+               return -1;
+       }
+       offset += write_buffer_len;
+       nr = 0;
        previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
 
        for (i = 0; i < entries; i++) {
@@ -2495,7 +2803,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                if (ce->ce_flags & CE_REMOVE)
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
-                       ce_smudge_racily_clean_entry(ce);
+                       ce_smudge_racily_clean_entry(istate, ce);
                if (is_null_oid(&ce->oid)) {
                        static const char msg[] = "cache entry has null sha1: %s";
                        static int allow = -1;
@@ -2509,23 +2817,79 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
 
                        drop_cache_tree = 1;
                }
+               if (ieot && i && (i % ieot_entries == 0)) {
+                       ieot->entries[ieot->nr].nr = nr;
+                       ieot->entries[ieot->nr].offset = offset;
+                       ieot->nr++;
+                       /*
+                        * If we have a V4 index, set the first byte to an invalid
+                        * character to ensure there is nothing common with the previous
+                        * entry
+                        */
+                       if (previous_name)
+                               previous_name->buf[0] = 0;
+                       nr = 0;
+                       offset = lseek(newfd, 0, SEEK_CUR);
+                       if (offset < 0) {
+                               free(ieot);
+                               return -1;
+                       }
+                       offset += write_buffer_len;
+               }
                if (ce_write_entry(&c, newfd, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0)
                        err = -1;
 
                if (err)
                        break;
+               nr++;
+       }
+       if (ieot && nr) {
+               ieot->entries[ieot->nr].nr = nr;
+               ieot->entries[ieot->nr].offset = offset;
+               ieot->nr++;
        }
        strbuf_release(&previous_name_buf);
 
-       if (err)
+       if (err) {
+               free(ieot);
                return err;
+       }
 
        /* Write extension data here */
+       offset = lseek(newfd, 0, SEEK_CUR);
+       if (offset < 0) {
+               free(ieot);
+               return -1;
+       }
+       offset += write_buffer_len;
+       the_hash_algo->init_fn(&eoie_c);
+
+       /*
+        * Lets write out CACHE_EXT_INDEXENTRYOFFSETTABLE first so that we
+        * can minimize the number of extensions we have to scan through to
+        * find it during load.  Write it out regardless of the
+        * strip_extensions parameter as we need it when loading the shared
+        * index.
+        */
+#ifndef NO_PTHREADS
+       if (ieot) {
+               struct strbuf sb = STRBUF_INIT;
+
+               write_ieot_extension(&sb, ieot);
+               err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_INDEXENTRYOFFSETTABLE, sb.len) < 0
+                       || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               strbuf_release(&sb);
+               free(ieot);
+               if (err)
+                       return -1;
+       }
+#endif
+
        if (!strip_extensions && istate->split_index) {
                struct strbuf sb = STRBUF_INIT;
 
                err = write_link_extension(&sb, istate) < 0 ||
-                       write_index_ext_header(&c, newfd, CACHE_EXT_LINK,
+                       write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_LINK,
                                               sb.len) < 0 ||
                        ce_write(&c, newfd, sb.buf, sb.len) < 0;
                strbuf_release(&sb);
@@ -2536,7 +2900,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                struct strbuf sb = STRBUF_INIT;
 
                cache_tree_write(&sb, istate->cache_tree);
-               err = write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sb.len) < 0
+               err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_TREE, sb.len) < 0
                        || ce_write(&c, newfd, sb.buf, sb.len) < 0;
                strbuf_release(&sb);
                if (err)
@@ -2546,7 +2910,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                struct strbuf sb = STRBUF_INIT;
 
                resolve_undo_write(&sb, istate->resolve_undo);
-               err = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO,
+               err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_RESOLVE_UNDO,
                                             sb.len) < 0
                        || ce_write(&c, newfd, sb.buf, sb.len) < 0;
                strbuf_release(&sb);
@@ -2557,7 +2921,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                struct strbuf sb = STRBUF_INIT;
 
                write_untracked_extension(&sb, istate->untracked);
-               err = write_index_ext_header(&c, newfd, CACHE_EXT_UNTRACKED,
+               err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_UNTRACKED,
                                             sb.len) < 0 ||
                        ce_write(&c, newfd, sb.buf, sb.len) < 0;
                strbuf_release(&sb);
@@ -2568,7 +2932,24 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                struct strbuf sb = STRBUF_INIT;
 
                write_fsmonitor_extension(&sb, istate);
-               err = write_index_ext_header(&c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
+               err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
+                       || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               strbuf_release(&sb);
+               if (err)
+                       return -1;
+       }
+
+       /*
+        * CACHE_EXT_ENDOFINDEXENTRIES must be written as the last entry before the SHA1
+        * so that it can be found and processed before all the index entries are
+        * read.  Write it out regardless of the strip_extensions parameter as we need it
+        * when loading the shared index.
+        */
+       if (offset) {
+               struct strbuf sb = STRBUF_INIT;
+
+               write_eoie_extension(&sb, &eoie_c, offset);
+               err = write_index_ext_header(&c, NULL, newfd, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0
                        || ce_write(&c, newfd, sb.buf, sb.len) < 0;
                strbuf_release(&sb);
                if (err)
@@ -2984,3 +3365,181 @@ int should_validate_cache_entries(void)
 
        return validate_index_cache_entries;
 }
+
+#define EOIE_SIZE (4 + GIT_SHA1_RAWSZ) /* <4-byte offset> + <20-byte hash> */
+#define EOIE_SIZE_WITH_HEADER (4 + 4 + EOIE_SIZE) /* <4-byte signature> + <4-byte length> + EOIE_SIZE */
+
+static size_t read_eoie_extension(const char *mmap, size_t mmap_size)
+{
+       /*
+        * The end of index entries (EOIE) extension is guaranteed to be last
+        * so that it can be found by scanning backwards from the EOF.
+        *
+        * "EOIE"
+        * <4-byte length>
+        * <4-byte offset>
+        * <20-byte hash>
+        */
+       const char *index, *eoie;
+       uint32_t extsize;
+       size_t offset, src_offset;
+       unsigned char hash[GIT_MAX_RAWSZ];
+       git_hash_ctx c;
+
+       /* ensure we have an index big enough to contain an EOIE extension */
+       if (mmap_size < sizeof(struct cache_header) + EOIE_SIZE_WITH_HEADER + the_hash_algo->rawsz)
+               return 0;
+
+       /* validate the extension signature */
+       index = eoie = mmap + mmap_size - EOIE_SIZE_WITH_HEADER - the_hash_algo->rawsz;
+       if (CACHE_EXT(index) != CACHE_EXT_ENDOFINDEXENTRIES)
+               return 0;
+       index += sizeof(uint32_t);
+
+       /* validate the extension size */
+       extsize = get_be32(index);
+       if (extsize != EOIE_SIZE)
+               return 0;
+       index += sizeof(uint32_t);
+
+       /*
+        * Validate the offset we're going to look for the first extension
+        * signature is after the index header and before the eoie extension.
+        */
+       offset = get_be32(index);
+       if (mmap + offset < mmap + sizeof(struct cache_header))
+               return 0;
+       if (mmap + offset >= eoie)
+               return 0;
+       index += sizeof(uint32_t);
+
+       /*
+        * The hash is computed over extension types and their sizes (but not
+        * their contents).  E.g. if we have "TREE" extension that is N-bytes
+        * long, "REUC" extension that is M-bytes long, followed by "EOIE",
+        * then the hash would be:
+        *
+        * SHA-1("TREE" + <binary representation of N> +
+        *       "REUC" + <binary representation of M>)
+        */
+       src_offset = offset;
+       the_hash_algo->init_fn(&c);
+       while (src_offset < mmap_size - the_hash_algo->rawsz - EOIE_SIZE_WITH_HEADER) {
+               /* After an array of active_nr index entries,
+                * there can be arbitrary number of extended
+                * sections, each of which is prefixed with
+                * extension name (4-byte) and section length
+                * in 4-byte network byte order.
+                */
+               uint32_t extsize;
+               memcpy(&extsize, mmap + src_offset + 4, 4);
+               extsize = ntohl(extsize);
+
+               /* verify the extension size isn't so large it will wrap around */
+               if (src_offset + 8 + extsize < src_offset)
+                       return 0;
+
+               the_hash_algo->update_fn(&c, mmap + src_offset, 8);
+
+               src_offset += 8;
+               src_offset += extsize;
+       }
+       the_hash_algo->final_fn(hash, &c);
+       if (!hasheq(hash, (const unsigned char *)index))
+               return 0;
+
+       /* Validate that the extension offsets returned us back to the eoie extension. */
+       if (src_offset != mmap_size - the_hash_algo->rawsz - EOIE_SIZE_WITH_HEADER)
+               return 0;
+
+       return offset;
+}
+
+static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset)
+{
+       uint32_t buffer;
+       unsigned char hash[GIT_MAX_RAWSZ];
+
+       /* offset */
+       put_be32(&buffer, offset);
+       strbuf_add(sb, &buffer, sizeof(uint32_t));
+
+       /* hash */
+       the_hash_algo->final_fn(hash, eoie_context);
+       strbuf_add(sb, hash, the_hash_algo->rawsz);
+}
+
+#ifndef NO_PTHREADS
+#define IEOT_VERSION   (1)
+
+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;
+}
+
+static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot)
+{
+       uint32_t buffer;
+       int i;
+
+       /* version */
+       put_be32(&buffer, IEOT_VERSION);
+       strbuf_add(sb, &buffer, sizeof(uint32_t));
+
+       /* ieot */
+       for (i = 0; i < ieot->nr; i++) {
+
+              /* 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));
+       }
+}
+#endif
diff --git a/rebase-interactive.c b/rebase-interactive.c
new file mode 100644 (file)
index 0000000..78f3263
--- /dev/null
@@ -0,0 +1,91 @@
+#include "cache.h"
+#include "commit.h"
+#include "rebase-interactive.h"
+#include "sequencer.h"
+#include "strbuf.h"
+
+void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+                     struct strbuf *buf)
+{
+       const char *msg = _("\nCommands:\n"
+"p, pick <commit> = use commit\n"
+"r, reword <commit> = use commit, but edit the commit message\n"
+"e, edit <commit> = use commit, but stop for amending\n"
+"s, squash <commit> = use commit, but meld into previous commit\n"
+"f, fixup <commit> = like \"squash\", but discard this commit's log message\n"
+"x, exec <command> = run command (the rest of the line) using shell\n"
+"b, break = stop here (continue rebase later with 'git rebase --continue')\n"
+"d, drop <commit> = remove commit\n"
+"l, label <label> = label current HEAD with a name\n"
+"t, reset <label> = reset HEAD to a label\n"
+"m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]\n"
+".       create a merge commit using the original merge commit's\n"
+".       message (or the oneline, if no original merge commit was\n"
+".       specified). Use -c <commit> to reword the commit message.\n"
+"\n"
+"These lines can be re-ordered; they are executed from top to bottom.\n");
+
+       strbuf_add_commented_lines(buf, msg, strlen(msg));
+
+       if (get_missing_commit_check_level() == MISSING_COMMIT_CHECK_ERROR)
+               msg = _("\nDo not remove any line. Use 'drop' "
+                        "explicitly to remove a commit.\n");
+       else
+               msg = _("\nIf you remove a line here "
+                        "THAT COMMIT WILL BE LOST.\n");
+
+       strbuf_add_commented_lines(buf, msg, strlen(msg));
+
+       if (edit_todo)
+               msg = _("\nYou are editing the todo file "
+                       "of an ongoing interactive rebase.\n"
+                       "To continue rebase after editing, run:\n"
+                       "    git rebase --continue\n\n");
+       else
+               msg = _("\nHowever, if you remove everything, "
+                       "the rebase will be aborted.\n\n");
+
+       strbuf_add_commented_lines(buf, msg, strlen(msg));
+
+       if (!keep_empty) {
+               msg = _("Note that empty commits are commented out");
+               strbuf_add_commented_lines(buf, msg, strlen(msg));
+       }
+}
+
+int edit_todo_list(unsigned flags)
+{
+       struct strbuf buf = STRBUF_INIT;
+       const char *todo_file = rebase_path_todo();
+
+       if (strbuf_read_file(&buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+
+       strbuf_stripspace(&buf, 1);
+       if (write_message(buf.buf, buf.len, todo_file, 0)) {
+               strbuf_release(&buf);
+               return -1;
+       }
+
+       strbuf_release(&buf);
+
+       transform_todos(flags | TODO_LIST_SHORTEN_IDS);
+
+       if (strbuf_read_file(&buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+
+       append_todo_help(1, 0, &buf);
+       if (write_message(buf.buf, buf.len, todo_file, 0)) {
+               strbuf_release(&buf);
+               return -1;
+       }
+
+       strbuf_release(&buf);
+
+       if (launch_sequence_editor(todo_file, NULL, NULL))
+               return -1;
+
+       transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS));
+
+       return 0;
+}
diff --git a/rebase-interactive.h b/rebase-interactive.h
new file mode 100644 (file)
index 0000000..971da03
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef REBASE_INTERACTIVE_H
+#define REBASE_INTERACTIVE_H
+
+void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+                     struct strbuf *buf);
+int edit_todo_list(unsigned flags);
+
+#endif
index e1bcb4ca8a1977e97b26da47962dfcd3781a021e..0c45ed9d94a4bd4bfab6c9d441ca9bc7de7ebf31 100644 (file)
@@ -875,7 +875,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
                if (deref)
                        name++;
                if (!strcmp(name, "objecttype"))
-                       v->s = type_name(oi->type);
+                       v->s = xstrdup(type_name(oi->type));
                else if (!strcmp(name, "objectsize")) {
                        v->value = oi->size;
                        v->s = xstrfmt("%lu", oi->size);
@@ -899,9 +899,9 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
                if (deref)
                        name++;
                if (!strcmp(name, "tag"))
-                       v->s = tag->tag;
+                       v->s = xstrdup(tag->tag);
                else if (!strcmp(name, "type") && tag->tagged)
-                       v->s = type_name(tag->tagged->type);
+                       v->s = xstrdup(type_name(tag->tagged->type));
                else if (!strcmp(name, "object") && tag->tagged)
                        v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
        }
@@ -1032,7 +1032,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
        v->value = timestamp;
        return;
  bad:
-       v->s = "";
+       v->s = xstrdup("");
        v->value = 0;
 }
 
@@ -1227,7 +1227,7 @@ static void fill_missing_values(struct atom_value *val)
        for (i = 0; i < used_atom_cnt; i++) {
                struct atom_value *v = &val[i];
                if (v->s == NULL)
-                       v->s = "";
+                       v->s = xstrdup("");
        }
 }
 
@@ -1273,7 +1273,8 @@ static inline char *copy_advance(char *dst, const char *src)
 static const char *lstrip_ref_components(const char *refname, int len)
 {
        long remaining = len;
-       const char *start = refname;
+       const char *start = xstrdup(refname);
+       const char *to_free = start;
 
        if (len < 0) {
                int i;
@@ -1294,20 +1295,24 @@ static const char *lstrip_ref_components(const char *refname, int len)
        while (remaining > 0) {
                switch (*start++) {
                case '\0':
-                       return "";
+                       free((char *)to_free);
+                       return xstrdup("");
                case '/':
                        remaining--;
                        break;
                }
        }
 
+       start = xstrdup(start);
+       free((char *)to_free);
        return start;
 }
 
 static const char *rstrip_ref_components(const char *refname, int len)
 {
        long remaining = len;
-       char *start = xstrdup(refname);
+       const char *start = xstrdup(refname);
+       const char *to_free = start;
 
        if (len < 0) {
                int i;
@@ -1327,9 +1332,10 @@ static const char *rstrip_ref_components(const char *refname, int len)
 
        while (remaining-- > 0) {
                char *p = strrchr(start, '/');
-               if (p == NULL)
-                       return "";
-               else
+               if (p == NULL) {
+                       free((char *)to_free);
+                       return xstrdup("");
+               } else
                        p[0] = '\0';
        }
        return start;
@@ -1344,7 +1350,7 @@ static const char *show_ref(struct refname_atom *atom, const char *refname)
        else if (atom->option == R_RSTRIP)
                return rstrip_ref_components(refname, atom->rstrip);
        else
-               return refname;
+               return xstrdup(refname);
 }
 
 static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
@@ -1358,7 +1364,7 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                                       NULL, AHEAD_BEHIND_FULL) < 0) {
                        *s = xstrdup(msgs.gone);
                } else if (!num_ours && !num_theirs)
-                       *s = "";
+                       *s = xstrdup("");
                else if (!num_ours)
                        *s = xstrfmt(msgs.behind, num_theirs);
                else if (!num_theirs)
@@ -1373,36 +1379,31 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                }
        } else if (atom->u.remote_ref.option == RR_TRACKSHORT) {
                if (stat_tracking_info(branch, &num_ours, &num_theirs,
-                                      NULL, AHEAD_BEHIND_FULL) < 0)
+                                      NULL, AHEAD_BEHIND_FULL) < 0) {
+                       *s = xstrdup("");
                        return;
-
+               }
                if (!num_ours && !num_theirs)
-                       *s = "=";
+                       *s = xstrdup("=");
                else if (!num_ours)
-                       *s = "<";
+                       *s = xstrdup("<");
                else if (!num_theirs)
-                       *s = ">";
+                       *s = xstrdup(">");
                else
-                       *s = "<>";
+                       *s = xstrdup("<>");
        } else if (atom->u.remote_ref.option == RR_REMOTE_NAME) {
                int explicit;
                const char *remote = atom->u.remote_ref.push ?
                        pushremote_for_branch(branch, &explicit) :
                        remote_for_branch(branch, &explicit);
-               if (explicit)
-                       *s = xstrdup(remote);
-               else
-                       *s = "";
+               *s = xstrdup(explicit ? remote : "");
        } else if (atom->u.remote_ref.option == RR_REMOTE_REF) {
                int explicit;
                const char *merge;
 
                merge = remote_ref_for_branch(branch, atom->u.remote_ref.push,
                                              &explicit);
-               if (explicit)
-                       *s = xstrdup(merge);
-               else
-                       *s = "";
+               *s = xstrdup(explicit ? merge : "");
        } else
                BUG("unhandled RR_* enum");
 }
@@ -1451,7 +1452,7 @@ char *get_head_description(void)
 static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref)
 {
        if (!ref->symref)
-               return "";
+               return xstrdup("");
        else
                return show_ref(&atom->u.refname, ref->symref);
 }
@@ -1510,7 +1511,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
                                             NULL, NULL);
                if (!ref->symref)
-                       ref->symref = "";
+                       ref->symref = xstrdup("");
        }
 
        /* Fill in specials first */
@@ -1536,20 +1537,23 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        refname = get_symref(atom, ref);
                else if (starts_with(name, "upstream")) {
                        const char *branch_name;
-                       v->s = "";
                        /* only local branches may have an upstream */
                        if (!skip_prefix(ref->refname, "refs/heads/",
-                                        &branch_name))
+                                        &branch_name)) {
+                               v->s = xstrdup("");
                                continue;
+                       }
                        branch = branch_get(branch_name);
 
                        refname = branch_get_upstream(branch, NULL);
                        if (refname)
                                fill_remote_ref_details(atom, refname, branch, &v->s);
+                       else
+                               v->s = xstrdup("");
                        continue;
                } else if (atom->u.remote_ref.push) {
                        const char *branch_name;
-                       v->s = "";
+                       v->s = xstrdup("");
                        if (!skip_prefix(ref->refname, "refs/heads/",
                                         &branch_name))
                                continue;
@@ -1562,10 +1566,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                                if (!refname)
                                        continue;
                        }
+                       /* We will definitely re-init v->s on the next line. */
+                       free((char *)v->s);
                        fill_remote_ref_details(atom, refname, branch, &v->s);
                        continue;
                } else if (starts_with(name, "color:")) {
-                       v->s = atom->u.color;
+                       v->s = xstrdup(atom->u.color);
                        continue;
                } else if (!strcmp(name, "flag")) {
                        char buf[256], *cp = buf;
@@ -1574,7 +1580,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        if (ref->flag & REF_ISPACKED)
                                cp = copy_advance(cp, ",packed");
                        if (cp == buf)
-                               v->s = "";
+                               v->s = xstrdup("");
                        else {
                                *cp = '\0';
                                v->s = xstrdup(buf + 1);
@@ -1584,40 +1590,42 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        continue;
                } else if (!strcmp(name, "HEAD")) {
                        if (atom->u.head && !strcmp(ref->refname, atom->u.head))
-                               v->s = "*";
+                               v->s = xstrdup("*");
                        else
-                               v->s = " ";
+                               v->s = xstrdup(" ");
                        continue;
                } else if (starts_with(name, "align")) {
                        v->handler = align_atom_handler;
-                       v->s = "";
+                       v->s = xstrdup("");
                        continue;
                } else if (!strcmp(name, "end")) {
                        v->handler = end_atom_handler;
-                       v->s = "";
+                       v->s = xstrdup("");
                        continue;
                } else if (starts_with(name, "if")) {
                        const char *s;
-                       v->s = "";
                        if (skip_prefix(name, "if:", &s))
                                v->s = xstrdup(s);
+                       else
+                               v->s = xstrdup("");
                        v->handler = if_atom_handler;
                        continue;
                } else if (!strcmp(name, "then")) {
                        v->handler = then_atom_handler;
-                       v->s = "";
+                       v->s = xstrdup("");
                        continue;
                } else if (!strcmp(name, "else")) {
                        v->handler = else_atom_handler;
-                       v->s = "";
+                       v->s = xstrdup("");
                        continue;
                } else
                        continue;
 
                if (!deref)
-                       v->s = refname;
+                       v->s = xstrdup(refname);
                else
                        v->s = xstrfmt("%s^{}", refname);
+               free((char *)refname);
        }
 
        for (i = 0; i < used_atom_cnt; i++) {
@@ -1988,6 +1996,10 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 static void free_array_item(struct ref_array_item *item)
 {
        free((char *)item->symref);
+       if (item->value) {
+               free((char *)item->value->s);
+               free(item->value);
+       }
        free(item);
 }
 
@@ -1996,6 +2008,10 @@ void ref_array_clear(struct ref_array *array)
 {
        int i;
 
+       for (i = 0; i < used_atom_cnt; i++)
+               free((char *)used_atom[i].name);
+       FREE_AND_NULL(used_atom);
+       used_atom_cnt = 0;
        for (i = 0; i < array->nr; i++)
                free_array_item(array->items[i]);
        FREE_AND_NULL(array->items);
@@ -2010,7 +2026,7 @@ static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
        struct ref_array *array = ref_cbdata->array;
        struct commit **to_clear = xcalloc(sizeof(struct commit *), array->nr);
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
 
        for (i = 0; i < array->nr; i++) {
                struct ref_array_item *item = array->items[i];
index 74e2996e93ad0033bd4650f8dd7d894a46d563b3..c01c7f5901a6f3bd0fd5fa638bfc286fa7e5f1d8 100644 (file)
@@ -274,8 +274,8 @@ struct snapshot_record {
 static int cmp_packed_ref_records(const void *v1, const void *v2)
 {
        const struct snapshot_record *e1 = v1, *e2 = v2;
-       const char *r1 = e1->start + GIT_SHA1_HEXSZ + 1;
-       const char *r2 = e2->start + GIT_SHA1_HEXSZ + 1;
+       const char *r1 = e1->start + the_hash_algo->hexsz + 1;
+       const char *r2 = e2->start + the_hash_algo->hexsz + 1;
 
        while (1) {
                if (*r1 == '\n')
@@ -297,7 +297,7 @@ static int cmp_packed_ref_records(const void *v1, const void *v2)
  */
 static int cmp_record_to_refname(const char *rec, const char *refname)
 {
-       const char *r1 = rec + GIT_SHA1_HEXSZ + 1;
+       const char *r1 = rec + the_hash_algo->hexsz + 1;
        const char *r2 = refname;
 
        while (1) {
@@ -344,7 +344,7 @@ static void sort_snapshot(struct snapshot *snapshot)
                if (!eol)
                        /* The safety check should prevent this. */
                        BUG("unterminated line found in packed-refs");
-               if (eol - pos < GIT_SHA1_HEXSZ + 2)
+               if (eol - pos < the_hash_algo->hexsz + 2)
                        die_invalid_line(snapshot->refs->path,
                                         pos, eof - pos);
                eol++;
@@ -456,7 +456,7 @@ static void verify_buffer_safe(struct snapshot *snapshot)
                return;
 
        last_line = find_start_of_record(start, eof - 1);
-       if (*(eof - 1) != '\n' || eof - last_line < GIT_SHA1_HEXSZ + 2)
+       if (*(eof - 1) != '\n' || eof - last_line < the_hash_algo->hexsz + 2)
                die_invalid_line(snapshot->refs->path,
                                 last_line, eof - last_line);
 }
@@ -796,7 +796,7 @@ static int next_record(struct packed_ref_iterator *iter)
 
        iter->base.flags = REF_ISPACKED;
 
-       if (iter->eof - p < GIT_SHA1_HEXSZ + 2 ||
+       if (iter->eof - p < the_hash_algo->hexsz + 2 ||
            parse_oid_hex(p, &iter->oid, &p) ||
            !isspace(*p++))
                die_invalid_line(iter->snapshot->refs->path,
@@ -826,7 +826,7 @@ static int next_record(struct packed_ref_iterator *iter)
 
        if (iter->pos < iter->eof && *iter->pos == '^') {
                p = iter->pos + 1;
-               if (iter->eof - p < GIT_SHA1_HEXSZ + 1 ||
+               if (iter->eof - p < the_hash_algo->hexsz + 1 ||
                    parse_oid_hex(p, &iter->peeled, &p) ||
                    *p++ != '\n')
                        die_invalid_line(iter->snapshot->refs->path,
index 682f2a01f949ce942597a9190cecfc991db0108e..81f4f01b00715b38f0d10fc03fad75f17aa1cf47 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1855,7 +1855,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
                         oid_to_hex(&theirs->object.oid));
        argv_array_push(&argv, "--");
 
-       init_revisions(&revs, NULL);
+       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");
index 7aa149e849775f110231a54cda7b71f60e75cb4f..b5b2357411f145867eb1918cbc79f77c6192db1a 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -29,7 +29,7 @@ static int rerere_dir_alloc;
 #define RR_HAS_POSTIMAGE 1
 #define RR_HAS_PREIMAGE 2
 static struct rerere_dir {
-       unsigned char sha1[20];
+       unsigned char hash[GIT_MAX_HEXSZ];
        int status_alloc, status_nr;
        unsigned char *status;
 } **rerere_dir;
@@ -52,7 +52,7 @@ static void free_rerere_id(struct string_list_item *item)
 
 static const char *rerere_id_hex(const struct rerere_id *id)
 {
-       return sha1_to_hex(id->collection->sha1);
+       return sha1_to_hex(id->collection->hash);
 }
 
 static void fit_variant(struct rerere_dir *rr_dir, int variant)
@@ -115,7 +115,7 @@ static int is_rr_file(const char *name, const char *filename, int *variant)
 static void scan_rerere_dir(struct rerere_dir *rr_dir)
 {
        struct dirent *de;
-       DIR *dir = opendir(git_path("rr-cache/%s", sha1_to_hex(rr_dir->sha1)));
+       DIR *dir = opendir(git_path("rr-cache/%s", sha1_to_hex(rr_dir->hash)));
 
        if (!dir)
                return;
@@ -133,24 +133,24 @@ static void scan_rerere_dir(struct rerere_dir *rr_dir)
        closedir(dir);
 }
 
-static const unsigned char *rerere_dir_sha1(size_t i, void *table)
+static const unsigned char *rerere_dir_hash(size_t i, void *table)
 {
        struct rerere_dir **rr_dir = table;
-       return rr_dir[i]->sha1;
+       return rr_dir[i]->hash;
 }
 
 static struct rerere_dir *find_rerere_dir(const char *hex)
 {
-       unsigned char sha1[20];
+       unsigned char hash[GIT_MAX_RAWSZ];
        struct rerere_dir *rr_dir;
        int pos;
 
-       if (get_sha1_hex(hex, sha1))
+       if (get_sha1_hex(hex, hash))
                return NULL; /* BUG */
-       pos = sha1_pos(sha1, rerere_dir, rerere_dir_nr, rerere_dir_sha1);
+       pos = sha1_pos(hash, rerere_dir, rerere_dir_nr, rerere_dir_hash);
        if (pos < 0) {
                rr_dir = xmalloc(sizeof(*rr_dir));
-               hashcpy(rr_dir->sha1, sha1);
+               hashcpy(rr_dir->hash, hash);
                rr_dir->status = NULL;
                rr_dir->status_nr = 0;
                rr_dir->status_alloc = 0;
@@ -207,26 +207,27 @@ static void read_rr(struct string_list *rr)
                return;
        while (!strbuf_getwholeline(&buf, in, '\0')) {
                char *path;
-               unsigned char sha1[20];
+               unsigned char hash[GIT_MAX_RAWSZ];
                struct rerere_id *id;
                int variant;
+               const unsigned hexsz = the_hash_algo->hexsz;
 
                /* There has to be the hash, tab, path and then NUL */
-               if (buf.len < 42 || get_sha1_hex(buf.buf, sha1))
+               if (buf.len < hexsz + 2 || get_sha1_hex(buf.buf, hash))
                        die(_("corrupt MERGE_RR"));
 
-               if (buf.buf[40] != '.') {
+               if (buf.buf[hexsz] != '.') {
                        variant = 0;
-                       path = buf.buf + 40;
+                       path = buf.buf + hexsz;
                } else {
                        errno = 0;
-                       variant = strtol(buf.buf + 41, &path, 10);
+                       variant = strtol(buf.buf + hexsz + 1, &path, 10);
                        if (errno)
                                die(_("corrupt MERGE_RR"));
                }
                if (*(path++) != '\t')
                        die(_("corrupt MERGE_RR"));
-               buf.buf[40] = '\0';
+               buf.buf[hexsz] = '\0';
                id = new_rerere_id_hex(buf.buf);
                id->variant = variant;
                string_list_insert(rr, path)->util = id;
@@ -360,7 +361,7 @@ static void rerere_strbuf_putconflict(struct strbuf *buf, int ch, size_t size)
 }
 
 static int handle_conflict(struct strbuf *out, struct rerere_io *io,
-                          int marker_size, git_SHA_CTX *ctx)
+                          int marker_size, git_hash_ctx *ctx)
 {
        enum {
                RR_SIDE_1 = 0, RR_SIDE_2, RR_ORIGINAL
@@ -398,10 +399,12 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,
                        strbuf_addbuf(out, &two);
                        rerere_strbuf_putconflict(out, '>', marker_size);
                        if (ctx) {
-                               git_SHA1_Update(ctx, one.buf ? one.buf : "",
-                                           one.len + 1);
-                               git_SHA1_Update(ctx, two.buf ? two.buf : "",
-                                           two.len + 1);
+                               the_hash_algo->update_fn(ctx, one.buf ?
+                                                        one.buf : "",
+                                                        one.len + 1);
+                               the_hash_algo->update_fn(ctx, two.buf ?
+                                                        two.buf : "",
+                                                        two.len + 1);
                        }
                        break;
                } else if (hunk == RR_SIDE_1)
@@ -430,18 +433,18 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,
  * Return 1 if conflict hunks are found, 0 if there are no conflict
  * hunks and -1 if an error occured.
  */
-static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_size)
+static int handle_path(unsigned char *hash, struct rerere_io *io, int marker_size)
 {
-       git_SHA_CTX ctx;
+       git_hash_ctx ctx;
        struct strbuf buf = STRBUF_INIT, out = STRBUF_INIT;
        int has_conflicts = 0;
-       if (sha1)
-               git_SHA1_Init(&ctx);
+       if (hash)
+               the_hash_algo->init_fn(&ctx);
 
        while (!io->getline(&buf, io)) {
                if (is_cmarker(buf.buf, '<', marker_size)) {
                        has_conflicts = handle_conflict(&out, io, marker_size,
-                                                       sha1 ? &ctx : NULL);
+                                                       hash ? &ctx : NULL);
                        if (has_conflicts < 0)
                                break;
                        rerere_io_putmem(out.buf, out.len, io);
@@ -452,8 +455,8 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
        strbuf_release(&buf);
        strbuf_release(&out);
 
-       if (sha1)
-               git_SHA1_Final(sha1, &ctx);
+       if (hash)
+               the_hash_algo->final_fn(hash, &ctx);
 
        return has_conflicts;
 }
@@ -462,11 +465,12 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
  * Scan the path for conflicts, do the "handle_path()" thing above, and
  * return the number of conflict hunks found.
  */
-static int handle_file(const char *path, unsigned char *sha1, const char *output)
+static int handle_file(struct index_state *istate,
+                      const char *path, unsigned char *hash, const char *output)
 {
        int has_conflicts = 0;
        struct rerere_io_file io;
-       int marker_size = ll_merge_marker_size(path);
+       int marker_size = ll_merge_marker_size(istate, path);
 
        memset(&io, 0, sizeof(io));
        io.io.getline = rerere_file_getline;
@@ -484,7 +488,7 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
                }
        }
 
-       has_conflicts = handle_path(sha1, (struct rerere_io *)&io, marker_size);
+       has_conflicts = handle_path(hash, (struct rerere_io *)&io, marker_size);
 
        fclose(io.input);
        if (io.io.wrerror)
@@ -511,9 +515,9 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
  * stages we have already looked at in this invocation of this
  * function.
  */
-static int check_one_conflict(int i, int *type)
+static int check_one_conflict(struct index_state *istate, int i, int *type)
 {
-       const struct cache_entry *e = active_cache[i];
+       const struct cache_entry *e = istate->cache[i];
 
        if (!ce_stage(e)) {
                *type = RESOLVED;
@@ -521,13 +525,13 @@ static int check_one_conflict(int i, int *type)
        }
 
        *type = PUNTED;
-       while (i < active_nr && ce_stage(active_cache[i]) == 1)
+       while (i < istate->cache_nr && ce_stage(istate->cache[i]) == 1)
                i++;
 
        /* Only handle regular files with both stages #2 and #3 */
-       if (i + 1 < active_nr) {
-               const struct cache_entry *e2 = active_cache[i];
-               const struct cache_entry *e3 = active_cache[i + 1];
+       if (i + 1 < istate->cache_nr) {
+               const struct cache_entry *e2 = istate->cache[i];
+               const struct cache_entry *e3 = istate->cache[i + 1];
                if (ce_stage(e2) == 2 &&
                    ce_stage(e3) == 3 &&
                    ce_same_name(e, e3) &&
@@ -537,7 +541,7 @@ static int check_one_conflict(int i, int *type)
        }
 
        /* Skip the entries with the same name */
-       while (i < active_nr && ce_same_name(e, active_cache[i]))
+       while (i < istate->cache_nr && ce_same_name(e, istate->cache[i]))
                i++;
        return i;
 }
@@ -553,16 +557,17 @@ static int check_one_conflict(int i, int *type)
  * are identical to the previous round, might want to be handled,
  * though.
  */
-static int find_conflict(struct string_list *conflict)
+static int find_conflict(struct repository *r, struct string_list *conflict)
 {
        int i;
-       if (read_cache() < 0)
+
+       if (read_index(r->index) < 0)
                return error(_("index file corrupt"));
 
-       for (i = 0; i < active_nr;) {
+       for (i = 0; i < r->index->cache_nr;) {
                int conflict_type;
-               const struct cache_entry *e = active_cache[i];
-               i = check_one_conflict(i, &conflict_type);
+               const struct cache_entry *e = r->index->cache[i];
+               i = check_one_conflict(r->index, i, &conflict_type);
                if (conflict_type == THREE_STAGED)
                        string_list_insert(conflict, (const char *)e->name);
        }
@@ -584,18 +589,19 @@ static int find_conflict(struct string_list *conflict)
  * NEEDSWORK: we may want to fix the caller that implements "rerere
  * remaining" to do this without abusing merge_rr.
  */
-int rerere_remaining(struct string_list *merge_rr)
+int rerere_remaining(struct repository *r, struct string_list *merge_rr)
 {
        int i;
+
        if (setup_rerere(merge_rr, RERERE_READONLY))
                return 0;
-       if (read_cache() < 0)
+       if (read_index(r->index) < 0)
                return error(_("index file corrupt"));
 
-       for (i = 0; i < active_nr;) {
+       for (i = 0; i < r->index->cache_nr;) {
                int conflict_type;
-               const struct cache_entry *e = active_cache[i];
-               i = check_one_conflict(i, &conflict_type);
+               const struct cache_entry *e = r->index->cache[i];
+               i = check_one_conflict(r->index, i, &conflict_type);
                if (conflict_type == PUNTED)
                        string_list_insert(merge_rr, (const char *)e->name);
                else if (conflict_type == RESOLVED) {
@@ -615,7 +621,8 @@ int rerere_remaining(struct string_list *merge_rr)
  * if that recorded conflict resolves cleanly what we
  * got in the "cur".
  */
-static int try_merge(const struct rerere_id *id, const char *path,
+static int try_merge(struct index_state *istate,
+                    const struct rerere_id *id, const char *path,
                     mmfile_t *cur, mmbuffer_t *result)
 {
        int ret;
@@ -629,7 +636,8 @@ static int try_merge(const struct rerere_id *id, const char *path,
                 * A three-way merge. Note that this honors user-customizable
                 * low-level merge driver settings.
                 */
-               ret = ll_merge(result, path, &base, NULL, cur, "", &other, "", NULL);
+               ret = ll_merge(result, path, &base, NULL, cur, "", &other, "",
+                              istate, NULL);
 
        free(base.ptr);
        free(other.ptr);
@@ -647,7 +655,7 @@ static int try_merge(const struct rerere_id *id, const char *path,
  * Returns 0 for successful replay of recorded resolution, or non-zero
  * for failure.
  */
-static int merge(const struct rerere_id *id, const char *path)
+static int merge(struct index_state *istate, const struct rerere_id *id, const char *path)
 {
        FILE *f;
        int ret;
@@ -658,13 +666,13 @@ static int merge(const struct rerere_id *id, const char *path)
         * Normalize the conflicts in path and write it out to
         * "thisimage" temporary file.
         */
-       if ((handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) ||
+       if ((handle_file(istate, path, NULL, rerere_path(id, "thisimage")) < 0) ||
            read_mmfile(&cur, rerere_path(id, "thisimage"))) {
                ret = 1;
                goto out;
        }
 
-       ret = try_merge(id, path, &cur, &result);
+       ret = try_merge(istate, id, path, &cur, &result);
        if (ret)
                goto out;
 
@@ -692,7 +700,7 @@ out:
        return ret;
 }
 
-static void update_paths(struct string_list *update)
+static void update_paths(struct repository *r, struct string_list *update)
 {
        struct lock_file index_lock = LOCK_INIT;
        int i;
@@ -701,13 +709,13 @@ static void update_paths(struct string_list *update)
 
        for (i = 0; i < update->nr; i++) {
                struct string_list_item *item = &update->items[i];
-               if (add_file_to_cache(item->string, 0))
+               if (add_file_to_index(r->index, item->string, 0))
                        exit(128);
                fprintf_ln(stderr, _("Staged '%s' using previous resolution."),
                        item->string);
        }
 
-       if (write_locked_index(&the_index, &index_lock,
+       if (write_locked_index(r->index, &index_lock,
                               COMMIT_LOCK | SKIP_IF_UNCHANGED))
                die(_("unable to write new index file"));
 }
@@ -726,7 +734,8 @@ static void remove_variant(struct rerere_id *id)
  * only have the preimage for that conflict, in which case the result
  * needs to be recorded as a resolution in a postimage file.
  */
-static void do_rerere_one_path(struct string_list_item *rr_item,
+static void do_rerere_one_path(struct index_state *istate,
+                              struct string_list_item *rr_item,
                               struct string_list *update)
 {
        const char *path = rr_item->string;
@@ -738,7 +747,7 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
 
        /* Has the user resolved it already? */
        if (variant >= 0) {
-               if (!handle_file(path, NULL, NULL)) {
+               if (!handle_file(istate, path, NULL, NULL)) {
                        copy_file(rerere_path(id, "postimage"), path, 0666);
                        id->collection->status[variant] |= RR_HAS_POSTIMAGE;
                        fprintf_ln(stderr, _("Recorded resolution for '%s'."), path);
@@ -762,7 +771,7 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
                        continue;
 
                vid.variant = variant;
-               if (merge(&vid, path))
+               if (merge(istate, &vid, path))
                        continue; /* failed to replay */
 
                /*
@@ -787,7 +796,7 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
        assign_variant(id);
 
        variant = id->variant;
-       handle_file(path, NULL, rerere_path(id, "preimage"));
+       handle_file(istate, path, NULL, rerere_path(id, "preimage"));
        if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
                const char *path = rerere_path(id, "postimage");
                if (unlink(path))
@@ -798,13 +807,14 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
        fprintf_ln(stderr, _("Recorded preimage for '%s'"), path);
 }
 
-static int do_plain_rerere(struct string_list *rr, int fd)
+static int do_plain_rerere(struct repository *r,
+                          struct string_list *rr, int fd)
 {
        struct string_list conflict = STRING_LIST_INIT_DUP;
        struct string_list update = STRING_LIST_INIT_DUP;
        int i;
 
-       find_conflict(&conflict);
+       find_conflict(r, &conflict);
 
        /*
         * MERGE_RR records paths with conflicts immediately after
@@ -814,7 +824,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
         */
        for (i = 0; i < conflict.nr; i++) {
                struct rerere_id *id;
-               unsigned char sha1[20];
+               unsigned char hash[GIT_MAX_RAWSZ];
                const char *path = conflict.items[i].string;
                int ret;
 
@@ -823,7 +833,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
                 * conflict ID.  No need to write anything out
                 * yet.
                 */
-               ret = handle_file(path, sha1, NULL);
+               ret = handle_file(r->index, path, hash, NULL);
                if (ret != 0 && string_list_has_string(rr, path)) {
                        remove_variant(string_list_lookup(rr, path)->util);
                        string_list_remove(rr, path, 1);
@@ -831,7 +841,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
                if (ret < 1)
                        continue;
 
-               id = new_rerere_id(sha1);
+               id = new_rerere_id(hash);
                string_list_insert(rr, path)->util = id;
 
                /* Ensure that the directory exists. */
@@ -839,10 +849,10 @@ static int do_plain_rerere(struct string_list *rr, int fd)
        }
 
        for (i = 0; i < rr->nr; i++)
-               do_rerere_one_path(&rr->items[i], &update);
+               do_rerere_one_path(r->index, &rr->items[i], &update);
 
        if (update.nr)
-               update_paths(&update);
+               update_paths(r, &update);
 
        return write_rr(rr, fd);
 }
@@ -897,7 +907,7 @@ int setup_rerere(struct string_list *merge_rr, int flags)
  * perform mergy operations, possibly leaving conflicted index entries
  * and working tree files.
  */
-int rerere(int flags)
+int repo_rerere(struct repository *r, int flags)
 {
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
        int fd, status;
@@ -905,7 +915,7 @@ int rerere(int flags)
        fd = setup_rerere(&merge_rr, flags);
        if (fd < 0)
                return 0;
-       status = do_plain_rerere(&merge_rr, fd);
+       status = do_plain_rerere(r, &merge_rr, fd);
        free_rerere_dirs();
        return status;
 }
@@ -942,29 +952,30 @@ static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
        return 0;
 }
 
-static int handle_cache(const char *path, unsigned char *sha1, const char *output)
+static int handle_cache(struct index_state *istate,
+                       const char *path, unsigned char *hash, const char *output)
 {
        mmfile_t mmfile[3] = {{NULL}};
        mmbuffer_t result = {NULL, 0};
        const struct cache_entry *ce;
        int pos, len, i, has_conflicts;
        struct rerere_io_mem io;
-       int marker_size = ll_merge_marker_size(path);
+       int marker_size = ll_merge_marker_size(istate, path);
 
        /*
         * Reproduce the conflicted merge in-core
         */
        len = strlen(path);
-       pos = cache_name_pos(path, len);
+       pos = index_name_pos(istate, path, len);
        if (0 <= pos)
                return -1;
        pos = -pos - 1;
 
-       while (pos < active_nr) {
+       while (pos < istate->cache_nr) {
                enum object_type type;
                unsigned long size;
 
-               ce = active_cache[pos++];
+               ce = istate->cache[pos++];
                if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
                        break;
                i = ce_stage(ce) - 1;
@@ -984,7 +995,8 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
         */
        ll_merge(&result, path, &mmfile[0], NULL,
                 &mmfile[1], "ours",
-                &mmfile[2], "theirs", NULL);
+                &mmfile[2], "theirs",
+                istate, NULL);
        for (i = 0; i < 3; i++)
                free(mmfile[i].ptr);
 
@@ -1001,18 +1013,20 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
         * Grab the conflict ID and optionally write the original
         * contents with conflict markers out.
         */
-       has_conflicts = handle_path(sha1, (struct rerere_io *)&io, marker_size);
+       has_conflicts = handle_path(hash, (struct rerere_io *)&io, marker_size);
        strbuf_release(&io.input);
        if (io.io.output)
                fclose(io.io.output);
        return has_conflicts;
 }
 
-static int rerere_forget_one_path(const char *path, struct string_list *rr)
+static int rerere_forget_one_path(struct index_state *istate,
+                                 const char *path,
+                                 struct string_list *rr)
 {
        const char *filename;
        struct rerere_id *id;
-       unsigned char sha1[20];
+       unsigned char hash[GIT_MAX_RAWSZ];
        int ret;
        struct string_list_item *item;
 
@@ -1020,12 +1034,12 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
         * Recreate the original conflict from the stages in the
         * index and compute the conflict ID
         */
-       ret = handle_cache(path, sha1, NULL);
+       ret = handle_cache(istate, path, hash, NULL);
        if (ret < 1)
                return error(_("could not parse conflict hunks in '%s'"), path);
 
        /* Nuke the recorded resolution for the conflict */
-       id = new_rerere_id(sha1);
+       id = new_rerere_id(hash);
 
        for (id->variant = 0;
             id->variant < id->collection->status_nr;
@@ -1037,13 +1051,13 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
                if (!has_rerere_resolution(id))
                        continue;
 
-               handle_cache(path, sha1, rerere_path(id, "thisimage"));
+               handle_cache(istate, path, hash, rerere_path(id, "thisimage"));
                if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
                        free(cur.ptr);
                        error(_("failed to update conflicted state in '%s'"), path);
                        goto fail_exit;
                }
-               cleanly_resolved = !try_merge(id, path, &cur, &result);
+               cleanly_resolved = !try_merge(istate, id, path, &cur, &result);
                free(result.ptr);
                free(cur.ptr);
                if (cleanly_resolved)
@@ -1069,7 +1083,7 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
         * conflict in the working tree, run us again to record
         * the postimage.
         */
-       handle_cache(path, sha1, rerere_path(id, "preimage"));
+       handle_cache(istate, path, hash, rerere_path(id, "preimage"));
        fprintf_ln(stderr, _("Updated preimage for '%s'"), path);
 
        /*
@@ -1087,13 +1101,13 @@ fail_exit:
        return -1;
 }
 
-int rerere_forget(struct pathspec *pathspec)
+int rerere_forget(struct repository *r, struct pathspec *pathspec)
 {
        int i, fd;
        struct string_list conflict = STRING_LIST_INIT_DUP;
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
 
-       if (read_cache() < 0)
+       if (read_index(r->index) < 0)
                return error(_("index file corrupt"));
 
        fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
@@ -1105,14 +1119,14 @@ int rerere_forget(struct pathspec *pathspec)
         * recover the original conflicted state and then
         * find the conflicted paths.
         */
-       unmerge_cache(pathspec);
-       find_conflict(&conflict);
+       unmerge_index(r->index, pathspec);
+       find_conflict(r, &conflict);
        for (i = 0; i < conflict.nr; i++) {
                struct string_list_item *it = &conflict.items[i];
-               if (!match_pathspec(&the_index, pathspec, it->string,
+               if (!match_pathspec(r->index, pathspec, it->string,
                                    strlen(it->string), 0, NULL, 0))
                        continue;
-               rerere_forget_one_path(it->string, &merge_rr);
+               rerere_forget_one_path(r->index, it->string, &merge_rr);
        }
        return write_rr(&merge_rr, fd);
 }
index cd948f28f494286c8e5750908eb7e35cf4293af3..5ad8864b3c71625a7fae5f67e625793d73e40fe4 100644 (file)
--- a/rerere.h
+++ b/rerere.h
@@ -4,6 +4,7 @@
 #include "string-list.h"
 
 struct pathspec;
+struct repository;
 
 #define RERERE_AUTOUPDATE   01
 #define RERERE_NOAUTOUPDATE 02
@@ -23,7 +24,10 @@ struct rerere_id {
 };
 
 int setup_rerere(struct string_list *, int);
-int rerere(int);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define rerere(flags) repo_rerere(the_repository, flags)
+#endif
+int repo_rerere(struct repository *, int);
 /*
  * Given the conflict ID and the name of a "file" used for replaying
  * the recorded resolution (e.g. "preimage", "postimage"), return the
@@ -31,8 +35,8 @@ int rerere(int);
  * return the path to the directory that houses these files.
  */
 const char *rerere_path(const struct rerere_id *, const char *file);
-int rerere_forget(struct pathspec *);
-int rerere_remaining(struct string_list *);
+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 *);
 
index e18bd530e4c50d0f5a1887714b074a6c2c63875b..a1ddb9e11cbe3a52bb8d3eee3785524db4055854 100644 (file)
@@ -52,7 +52,8 @@ static void mark_blob_uninteresting(struct blob *blob)
        blob->object.flags |= UNINTERESTING;
 }
 
-static void mark_tree_contents_uninteresting(struct tree *tree)
+static void mark_tree_contents_uninteresting(struct repository *r,
+                                            struct tree *tree)
 {
        struct tree_desc desc;
        struct name_entry entry;
@@ -64,10 +65,10 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
-                       mark_tree_uninteresting(lookup_tree(the_repository, entry.oid));
+                       mark_tree_uninteresting(r, lookup_tree(r, entry.oid));
                        break;
                case OBJ_BLOB:
-                       mark_blob_uninteresting(lookup_blob(the_repository, entry.oid));
+                       mark_blob_uninteresting(lookup_blob(r, entry.oid));
                        break;
                default:
                        /* Subproject commit - not in this repository */
@@ -82,7 +83,7 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
        free_tree_buffer(tree);
 }
 
-void mark_tree_uninteresting(struct tree *tree)
+void mark_tree_uninteresting(struct repository *r, struct tree *tree)
 {
        struct object *obj;
 
@@ -93,7 +94,7 @@ void mark_tree_uninteresting(struct tree *tree)
        if (obj->flags & UNINTERESTING)
                return;
        obj->flags |= UNINTERESTING;
-       mark_tree_contents_uninteresting(tree);
+       mark_tree_contents_uninteresting(r, tree);
 }
 
 struct commit_stack {
@@ -176,7 +177,6 @@ static void add_pending_object_with_path(struct rev_info *revs,
                strbuf_release(&buf);
                return; /* do not add the commit itself */
        }
-       obj->flags |= USER_GIVEN;
        add_object_array_with_path(obj, name, &revs->pending, mode, path);
 }
 
@@ -199,7 +199,7 @@ void add_head_to_pending(struct rev_info *revs)
        struct object *obj;
        if (get_oid("HEAD", &oid))
                return;
-       obj = parse_object(the_repository, &oid);
+       obj = parse_object(revs->repo, &oid);
        if (!obj)
                return;
        add_pending_object(revs, obj, "HEAD");
@@ -211,7 +211,7 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
 {
        struct object *object;
 
-       object = parse_object(the_repository, oid);
+       object = parse_object(revs->repo, oid);
        if (!object) {
                if (revs->ignore_missing)
                        return object;
@@ -248,7 +248,7 @@ static struct commit *handle_commit(struct rev_info *revs,
                        add_pending_object(revs, object, tag->tag);
                if (!tag->tagged)
                        die("bad tag");
-               object = parse_object(the_repository, &tag->tagged->oid);
+               object = parse_object(revs->repo, &tag->tagged->oid);
                if (!object) {
                        if (revs->ignore_missing_links || (flags & UNINTERESTING))
                                return NULL;
@@ -298,7 +298,7 @@ static struct commit *handle_commit(struct rev_info *revs,
                if (!revs->tree_objects)
                        return NULL;
                if (flags & UNINTERESTING) {
-                       mark_tree_contents_uninteresting(tree);
+                       mark_tree_contents_uninteresting(revs->repo, tree);
                        return NULL;
                }
                add_pending_object_with_path(revs, object, name, mode, path);
@@ -878,7 +878,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
                return;
 
        left_first = left_count < right_count;
-       init_patch_ids(&ids);
+       init_patch_ids(revs->repo, &ids);
        ids.diffopts.pathspec = revs->diffopt.pathspec;
 
        /* Compute patch-ids for one side */
@@ -1254,7 +1254,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
 {
        struct all_refs_cb *cb = cb_data;
        if (!is_null_oid(oid)) {
-               struct object *o = parse_object(the_repository, oid);
+               struct object *o = parse_object(cb->all_revs->repo, oid);
                if (o) {
                        o->flags |= cb->all_flags;
                        /* ??? CMDLINEFLAGS ??? */
@@ -1313,7 +1313,7 @@ void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
 
        cb.all_revs = revs;
        cb.all_flags = flags;
-       cb.refs = get_main_ref_store(the_repository);
+       cb.refs = get_main_ref_store(revs->repo);
        for_each_reflog(handle_one_reflog, &cb);
 
        if (!revs->single_worktree)
@@ -1327,7 +1327,7 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
        int i;
 
        if (it->entry_count >= 0) {
-               struct tree *tree = lookup_tree(the_repository, &it->oid);
+               struct tree *tree = lookup_tree(revs->repo, &it->oid);
                add_pending_object_with_path(revs, &tree->object, "",
                                             040000, path->buf);
        }
@@ -1353,7 +1353,7 @@ static void do_add_index_objects_to_pending(struct rev_info *revs,
                if (S_ISGITLINK(ce->ce_mode))
                        continue;
 
-               blob = lookup_blob(the_repository, &ce->oid);
+               blob = lookup_blob(revs->repo, &ce->oid);
                if (!blob)
                        die("unable to add index blob to traversal");
                add_pending_object_with_path(revs, &blob->object, "",
@@ -1371,8 +1371,8 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
 {
        struct worktree **worktrees, **p;
 
-       read_cache();
-       do_add_index_objects_to_pending(revs, &the_index);
+       read_index(revs->repo->index);
+       do_add_index_objects_to_pending(revs, revs->repo->index);
 
        if (revs->single_worktree)
                return;
@@ -1440,10 +1440,13 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
        return 1;
 }
 
-void init_revisions(struct rev_info *revs, const char *prefix)
+void repo_init_revisions(struct repository *r,
+                        struct rev_info *revs,
+                        const char *prefix)
 {
        memset(revs, 0, sizeof(*revs));
 
+       revs->repo = r;
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
@@ -1465,11 +1468,11 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->commit_format = CMIT_FMT_DEFAULT;
        revs->expand_tabs_in_log_default = 8;
 
-       init_grep_defaults();
-       grep_init(&revs->grep_filter, prefix);
+       init_grep_defaults(revs->repo);
+       grep_init(&revs->grep_filter, revs->repo, prefix);
        revs->grep_filter.status_only = 1;
 
-       diff_setup(&revs->diffopt);
+       repo_diff_setup(revs->repo, &revs->diffopt);
        if (prefix && !revs->diffopt.prefix) {
                revs->diffopt.prefix = prefix;
                revs->diffopt.prefix_length = strlen(prefix);
@@ -1497,6 +1500,7 @@ static void prepare_show_merge(struct rev_info *revs)
        struct object_id oid;
        const char **prune = NULL;
        int i, prune_num = 1; /* counting terminating NULL */
+       struct index_state *istate = revs->repo->index;
 
        if (get_oid("HEAD", &oid))
                die("--merge without HEAD?");
@@ -1512,20 +1516,20 @@ static void prepare_show_merge(struct rev_info *revs)
        free_commit_list(bases);
        head->object.flags |= SYMMETRIC_LEFT;
 
-       if (!active_nr)
-               read_cache();
-       for (i = 0; i < active_nr; i++) {
-               const struct cache_entry *ce = active_cache[i];
+       if (!istate->cache_nr)
+               read_index(istate);
+       for (i = 0; i < istate->cache_nr; i++) {
+               const struct cache_entry *ce = istate->cache[i];
                if (!ce_stage(ce))
                        continue;
-               if (ce_path_match(&the_index, ce, &revs->prune_data, NULL)) {
+               if (ce_path_match(istate, ce, &revs->prune_data, NULL)) {
                        prune_num++;
                        REALLOC_ARRAY(prune, prune_num);
                        prune[prune_num-2] = ce->name;
                        prune[prune_num-1] = NULL;
                }
-               while ((i+1 < active_nr) &&
-                      ce_same_name(ce, active_cache[i+1]))
+               while ((i+1 < istate->cache_nr) &&
+                      ce_same_name(ce, istate->cache[i+1]))
                        i++;
        }
        clear_pathspec(&revs->prune_data);
@@ -1582,8 +1586,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
                *dotdot = '\0';
        }
 
-       a_obj = parse_object(the_repository, &a_oid);
-       b_obj = parse_object(the_repository, &b_oid);
+       a_obj = parse_object(revs->repo, &a_oid);
+       b_obj = parse_object(revs->repo, &b_oid);
        if (!a_obj || !b_obj)
                return dotdot_missing(arg, dotdot, revs, symmetric);
 
@@ -1596,8 +1600,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
                struct commit *a, *b;
                struct commit_list *exclude;
 
-               a = lookup_commit_reference(the_repository, &a_obj->oid);
-               b = lookup_commit_reference(the_repository, &b_obj->oid);
+               a = lookup_commit_reference(revs->repo, &a_obj->oid);
+               b = lookup_commit_reference(revs->repo, &b_obj->oid);
                if (!a || !b)
                        return dotdot_missing(arg, dotdot, revs, symmetric);
 
@@ -2205,7 +2209,7 @@ static int handle_revision_pseudo_opt(const char *submodule,
                        BUG("--single-worktree cannot be used together with submodule");
                refs = get_submodule_ref_store(submodule);
        } else
-               refs = get_main_ref_store(the_repository);
+               refs = get_main_ref_store(revs->repo);
 
        /*
         * NOTE!
@@ -2885,9 +2889,10 @@ void reset_revision_walk(void)
 static int mark_uninteresting(const struct object_id *oid,
                              struct packed_git *pack,
                              uint32_t pos,
-                             void *unused)
+                             void *cb)
 {
-       struct object *o = parse_object(the_repository, oid);
+       struct rev_info *revs = cb;
+       struct object *o = parse_object(revs->repo, oid);
        o->flags |= UNINTERESTING | SEEN;
        return 0;
 }
@@ -2920,7 +2925,7 @@ int prepare_revision_walk(struct rev_info *revs)
                revs->treesame.name = "treesame";
 
        if (revs->exclude_promisor_objects) {
-               for_each_packed_object(mark_uninteresting, NULL,
+               for_each_packed_object(mark_uninteresting, revs,
                                       FOR_EACH_OBJECT_PROMISOR_ONLY);
        }
 
index 2b30ac270d9295e00641b08483fe07cd44dc51f6..1cd0c4b200887e6b73d6a2473712bb5832f7e079 100644 (file)
 #define SYMMETRIC_LEFT (1u<<8)
 #define PATCHSAME      (1u<<9)
 #define BOTTOM         (1u<<10)
-#define USER_GIVEN     (1u<<25) /* given directly by the user */
+/*
+ * Indicates object was reached by traversal. i.e. not given by user on
+ * command-line or stdin.
+ * NEEDSWORK: NOT_USER_GIVEN doesn't apply to commits because we only support
+ * filtering trees and blobs, but it may be useful to support filtering commits
+ * in the future.
+ */
+#define NOT_USER_GIVEN (1u<<25)
 #define TRACK_LINEAR   (1u<<26)
-#define ALL_REV_FLAGS  (((1u<<11)-1) | USER_GIVEN | TRACK_LINEAR)
+#define ALL_REV_FLAGS  (((1u<<11)-1) | NOT_USER_GIVEN | TRACK_LINEAR)
 
 #define DECORATE_SHORT_REFS    1
 #define DECORATE_FULL_REFS     2
 
-struct rev_info;
 struct log_info;
+struct repository;
+struct rev_info;
 struct string_list;
 struct saved_parents;
 define_shared_commit_slab(revision_sources, char *);
@@ -60,6 +68,7 @@ struct rev_info {
        /* Starting list */
        struct commit_list *commits;
        struct object_array pending;
+       struct repository *repo;
 
        /* Parents of shown commits */
        struct object_array boundary_commits;
@@ -131,6 +140,21 @@ struct rev_info {
                        line_level_traverse:1,
                        tree_blobs_in_commit_order:1,
 
+                       /*
+                        * Blobs are shown without regard for their existence.
+                        * But not so for trees: unless exclude_promisor_objects
+                        * is set and the tree in question is a promisor object;
+                        * OR ignore_missing_links is set, the revision walker
+                        * dies with a "bad tree object HASH" message when
+                        * encountering a missing tree. For callers that can
+                        * handle missing trees and want them to be filterable
+                        * and showable, set this to true. The revision walker
+                        * will filter and show such a missing tree as usual,
+                        * but will not attempt to recurse into this tree
+                        * object.
+                        */
+                       do_not_die_on_missing_tree:1,
+
                        /* for internal use only */
                        exclude_promisor_objects:1;
 
@@ -264,12 +288,17 @@ extern volatile show_early_output_fn_t show_early_output;
 struct setup_revision_opt {
        const char *def;
        void (*tweak)(struct rev_info *, struct setup_revision_opt *);
-       const char *submodule;
+       const char *submodule;  /* TODO: drop this and use rev_info->repo */
        int assume_dashdash;
        unsigned revarg_opt;
 };
 
-void init_revisions(struct rev_info *revs, const char *prefix);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
+#endif
+void repo_init_revisions(struct repository *r,
+                        struct rev_info *revs,
+                        const char *prefix);
 int setup_revisions(int argc, const char **argv, struct rev_info *revs,
                    struct setup_revision_opt *);
 void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
@@ -289,7 +318,7 @@ void put_revision_mark(const struct rev_info *revs,
                       const struct commit *commit);
 
 void mark_parents_uninteresting(struct commit *commit);
-void mark_tree_uninteresting(struct tree *tree);
+void mark_tree_uninteresting(struct repository *r, struct tree *tree);
 
 void show_object_with_name(FILE *, struct object *, const char *);
 
index 84b883c2132bb07bc7fa21434368d64664587b8b..d679cc267c39b9fb34f57078db5db12ed591b7e2 100644 (file)
@@ -380,7 +380,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
        set_error_routine(old_errfn);
 }
 
-static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
+static int prepare_cmd(struct argv_array *out, const struct child_process *cmd)
 {
        if (!cmd->argv[0])
                BUG("command is empty");
@@ -403,16 +403,22 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
        /*
         * If there are no '/' characters in the command then perform a path
         * lookup and use the resolved path as the command to exec.  If there
-        * are no '/' characters or if the command wasn't found in the path,
-        * have exec attempt to invoke the command directly.
+        * are '/' characters, we have exec attempt to invoke the command
+        * directly.
         */
        if (!strchr(out->argv[1], '/')) {
                char *program = locate_in_PATH(out->argv[1]);
                if (program) {
                        free((char *)out->argv[1]);
                        out->argv[1] = program;
+               } else {
+                       argv_array_clear(out);
+                       errno = ENOENT;
+                       return -1;
                }
        }
+
+       return 0;
 }
 
 static char **prep_childenv(const char *const *deltaenv)
@@ -719,6 +725,12 @@ fail_pipe:
        struct child_err cerr;
        struct atfork_state as;
 
+       if (prepare_cmd(&argv, cmd) < 0) {
+               failed_errno = errno;
+               cmd->pid = -1;
+               goto end_of_spawn;
+       }
+
        if (pipe(notify_pipe))
                notify_pipe[0] = notify_pipe[1] = -1;
 
@@ -729,7 +741,6 @@ fail_pipe:
                set_cloexec(null_fd);
        }
 
-       prepare_cmd(&argv, cmd);
        childenv = prep_childenv(cmd->env);
        atfork_prepare(&as);
 
@@ -857,6 +868,8 @@ fail_pipe:
        argv_array_clear(&argv);
        free(childenv);
 }
+end_of_spawn:
+
 #else
 {
        int fhin = 0, fhout = 1, fherr = 2;
index 6387c9ee6e8ba20aa276cf3fb9620d2caed70079..9e1ab3a2a7e3f65cc444cded7f3330bb27be2db8 100644 (file)
@@ -31,6 +31,7 @@
 #include "commit-slab.h"
 #include "alias.h"
 #include "commit-reach.h"
+#include "rebase-interactive.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -53,7 +54,10 @@ static GIT_PATH_FUNC(rebase_path, "rebase-merge")
  * the lines are processed, they are removed from the front of this
  * file and written to the tail of 'done'.
  */
-static GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
+GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
+static GIT_PATH_FUNC(rebase_path_todo_backup,
+                    "rebase-merge/git-rebase-todo.backup")
+
 /*
  * The rebase command lines that have already been processed. A line
  * is moved here when it is first handled, before any associated user
@@ -141,7 +145,7 @@ static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs-to-delete")
 
 /*
  * The following files are written by git-rebase just after parsing the
- * command-line (and are only consumed, not modified, by the sequencer).
+ * command-line.
  */
 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")
@@ -153,6 +157,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 int git_sequencer_config(const char *k, const char *v, void *cb)
 {
@@ -377,8 +382,8 @@ static void print_advice(int show_hint, struct replay_opts *opts)
        }
 }
 
-static int write_message(const void *buf, size_t len, const char *filename,
-                        int append_eol)
+int write_message(const void *buf, size_t len, const char *filename,
+                 int append_eol)
 {
        struct lock_file msg_file = LOCK_INIT;
 
@@ -474,8 +479,8 @@ static int fast_forward_to(const struct object_id *to, const struct object_id *f
        struct strbuf sb = STRBUF_INIT;
        struct strbuf err = STRBUF_INIT;
 
-       read_cache();
-       if (checkout_fast_forward(from, to, 1))
+       read_index(&the_index);
+       if (checkout_fast_forward(the_repository, from, to, 1))
                return -1; /* the callee should have complained already */
 
        strbuf_addf(&sb, _("%s: fast-forward"), _(action_name(opts)));
@@ -804,6 +809,23 @@ N_("you have staged changes in your working tree\n"
 #define VERIFY_MSG  (1<<4)
 #define CREATE_ROOT_COMMIT (1<<5)
 
+static int run_command_silent_on_success(struct child_process *cmd)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int rc;
+
+       cmd->stdout_to_stderr = 1;
+       rc = pipe_command(cmd,
+                         NULL, 0,
+                         NULL, 0,
+                         &buf, 0);
+
+       if (rc)
+               fputs(buf.buf, stderr);
+       strbuf_release(&buf);
+       return rc;
+}
+
 /*
  * If we are cherry-pick, and if the merge did not result in
  * hand-editing, we will hit this commit and inherit the original
@@ -865,18 +887,11 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 
        cmd.git_cmd = 1;
 
-       if (is_rebase_i(opts)) {
-               if (!(flags & EDIT_MSG)) {
-                       cmd.stdout_to_stderr = 1;
-                       cmd.err = -1;
-               }
-
-               if (read_env_script(&cmd.env_array)) {
-                       const char *gpg_opt = gpg_sign_opt_quoted(opts);
+       if (is_rebase_i(opts) && read_env_script(&cmd.env_array)) {
+               const char *gpg_opt = gpg_sign_opt_quoted(opts);
 
-                       return error(_(staged_changes_advice),
-                                    gpg_opt, gpg_opt);
-               }
+               return error(_(staged_changes_advice),
+                            gpg_opt, gpg_opt);
        }
 
        argv_array_push(&cmd.args, "commit");
@@ -906,21 +921,10 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
        if (!(flags & EDIT_MSG))
                argv_array_push(&cmd.args, "--allow-empty-message");
 
-       if (cmd.err == -1) {
-               /* hide stderr on success */
-               struct strbuf buf = STRBUF_INIT;
-               int rc = pipe_command(&cmd,
-                                     NULL, 0,
-                                     /* stdout is already redirected */
-                                     NULL, 0,
-                                     &buf, 0);
-               if (rc)
-                       fputs(buf.buf, stderr);
-               strbuf_release(&buf);
-               return rc;
-       }
-
-       return run_command(&cmd);
+       if (is_rebase_i(opts) && !(flags & EDIT_MSG))
+               return run_command_silent_on_success(&cmd);
+       else
+               return run_command(&cmd);
 }
 
 static int rest_is_empty(const struct strbuf *sb, int start)
@@ -1176,7 +1180,7 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
        strbuf_release(&author_ident);
        strbuf_release(&committer_ident);
 
-       init_revisions(&rev, prefix);
+       repo_init_revisions(the_repository, &rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
 
        rev.diff = 1;
@@ -1454,6 +1458,7 @@ enum todo_command {
        TODO_SQUASH,
        /* commands that do something else than handling a single commit */
        TODO_EXEC,
+       TODO_BREAK,
        TODO_LABEL,
        TODO_RESET,
        TODO_MERGE,
@@ -1475,6 +1480,7 @@ static struct {
        { 'f', "fixup" },
        { 's', "squash" },
        { 'x', "exec" },
+       { 'b', "break" },
        { 'l', "label" },
        { 't', "reset" },
        { 'm', "merge" },
@@ -1831,7 +1837,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
-               res |= try_merge_command(opts->strategy,
+               res |= try_merge_command(the_repository, opts->strategy,
                                         opts->xopts_nr, (const char **)opts->xopts,
                                        common, oid_to_hex(&head), remotes);
                free_commit_list(common);
@@ -1860,7 +1866,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                      : _("could not apply %s... %s"),
                      short_commit_name(commit), msg.subject);
                print_advice(res == 1, opts);
-               rerere(opts->allow_rerere_auto);
+               repo_rerere(the_repository, opts->allow_rerere_auto);
                goto leave;
        }
 
@@ -1913,7 +1919,7 @@ static int read_and_refresh_cache(struct replay_opts *opts)
 {
        struct lock_file index_lock = LOCK_INIT;
        int index_fd = hold_locked_index(&index_lock, 0);
-       if (read_index_preload(&the_index, NULL) < 0) {
+       if (read_index_preload(&the_index, NULL, 0) < 0) {
                rollback_lock_file(&index_lock);
                return error(_("git %s: failed to read the index"),
                        _(action_name(opts)));
@@ -1988,7 +1994,8 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
                if (skip_prefix(bol, todo_command_info[i].str, &bol)) {
                        item->command = i;
                        break;
-               } else if (bol[1] == ' ' && *bol == todo_command_info[i].c) {
+               } else if ((bol + 1 == eol || bol[1] == ' ') &&
+                          *bol == todo_command_info[i].c) {
                        bol++;
                        item->command = i;
                        break;
@@ -2000,7 +2007,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
        padding = strspn(bol, " \t");
        bol += padding;
 
-       if (item->command == TODO_NOOP) {
+       if (item->command == TODO_NOOP || item->command == TODO_BREAK) {
                if (bol != eol)
                        return error(_("%s does not accept arguments: '%s'"),
                                     command_to_string(item->command), bol);
@@ -2244,21 +2251,14 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
        return 0;
 }
 
-static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
+void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
 {
        int i;
-       char *strategy_opts_string;
-
-       strbuf_reset(buf);
-       if (!read_oneliner(buf, rebase_path_strategy(), 0))
-               return;
-       opts->strategy = strbuf_detach(buf, NULL);
-       if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
-               return;
+       char *strategy_opts_string = raw_opts;
 
-       strategy_opts_string = buf->buf;
        if (*strategy_opts_string == ' ')
                strategy_opts_string++;
+
        opts->xopts_nr = split_cmdline(strategy_opts_string,
                                       (const char ***)&opts->xopts);
        for (i = 0; i < opts->xopts_nr; i++) {
@@ -2269,6 +2269,18 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
        }
 }
 
+static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
+{
+       strbuf_reset(buf);
+       if (!read_oneliner(buf, rebase_path_strategy(), 0))
+               return;
+       opts->strategy = strbuf_detach(buf, NULL);
+       if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
+               return;
+
+       parse_strategy_opts(opts, buf->buf);
+}
+
 static int read_populate_opts(struct replay_opts *opts)
 {
        if (is_rebase_i(opts)) {
@@ -2336,6 +2348,55 @@ static int read_populate_opts(struct replay_opts *opts)
        return 0;
 }
 
+static void write_strategy_opts(struct replay_opts *opts)
+{
+       int i;
+       struct strbuf buf = STRBUF_INIT;
+
+       for (i = 0; i < opts->xopts_nr; ++i)
+               strbuf_addf(&buf, " --%s", opts->xopts[i]);
+
+       write_file(rebase_path_strategy_opts(), "%s\n", buf.buf);
+       strbuf_release(&buf);
+}
+
+int write_basic_state(struct replay_opts *opts, const char *head_name,
+                     const char *onto, const char *orig_head)
+{
+       const char *quiet = getenv("GIT_QUIET");
+
+       if (head_name)
+               write_file(rebase_path_head_name(), "%s\n", head_name);
+       if (onto)
+               write_file(rebase_path_onto(), "%s\n", onto);
+       if (orig_head)
+               write_file(rebase_path_orig_head(), "%s\n", orig_head);
+
+       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)
+               write_file(rebase_path_strategy(), "%s\n", opts->strategy);
+       if (opts->xopts_nr > 0)
+               write_strategy_opts(opts);
+
+       if (opts->allow_rerere_auto == RERERE_AUTOUPDATE)
+               write_file(rebase_path_allow_rerere_autoupdate(), "--rerere-autoupdate\n");
+       else if (opts->allow_rerere_auto == RERERE_NOAUTOUPDATE)
+               write_file(rebase_path_allow_rerere_autoupdate(), "--no-rerere-autoupdate\n");
+
+       if (opts->gpg_sign)
+               write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
+       if (opts->signoff)
+               write_file(rebase_path_signoff(), "--signoff\n");
+
+       return 0;
+}
+
 static int walk_revs_populate_todo(struct todo_list *todo_list,
                                struct replay_opts *opts)
 {
@@ -2599,7 +2660,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));
-       init_revisions(&log_tree_opt, NULL);
+       repo_init_revisions(the_repository, &log_tree_opt, NULL);
        log_tree_opt.abbrev = 0;
        log_tree_opt.diff = 1;
        log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;
@@ -3179,7 +3240,7 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
 
        rollback_lock_file(&lock);
        if (ret)
-               rerere(opts->allow_rerere_auto);
+               repo_rerere(the_repository, opts->allow_rerere_auto);
        else
                /*
                 * In case of problems, we now want to return a positive
@@ -3286,6 +3347,73 @@ static const char *reflog_message(struct replay_opts *opts,
        return buf.buf;
 }
 
+static int run_git_checkout(struct replay_opts *opts, const char *commit,
+                           const char *action)
+{
+       struct child_process cmd = CHILD_PROCESS_INIT;
+
+       cmd.git_cmd = 1;
+
+       argv_array_push(&cmd.args, "checkout");
+       argv_array_push(&cmd.args, commit);
+       argv_array_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
+
+       if (opts->verbose)
+               return run_command(&cmd);
+       else
+               return run_command_silent_on_success(&cmd);
+}
+
+int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit)
+{
+       const char *action;
+
+       if (commit && *commit) {
+               action = reflog_message(opts, "start", "checkout %s", commit);
+               if (run_git_checkout(opts, commit, action))
+                       return error(_("could not checkout %s"), commit);
+       }
+
+       return 0;
+}
+
+static int checkout_onto(struct replay_opts *opts,
+                        const char *onto_name, const char *onto,
+                        const char *orig_head)
+{
+       struct object_id oid;
+       const char *action = reflog_message(opts, "start", "checkout %s", onto_name);
+
+       if (get_oid(orig_head, &oid))
+               return error(_("%s: not a valid OID"), orig_head);
+
+       if (run_git_checkout(opts, onto, action)) {
+               apply_autostash(opts);
+               sequencer_remove_state(opts);
+               return error(_("could not detach HEAD"));
+       }
+
+       return update_ref(NULL, "ORIG_HEAD", &oid, NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+}
+
+static int stopped_at_head(void)
+{
+       struct object_id head;
+       struct commit *commit;
+       struct commit_message message;
+
+       if (get_oid("HEAD", &head) ||
+           !(commit = lookup_commit(the_repository, &head)) ||
+           parse_commit(commit) || get_message(commit, &message))
+               fprintf(stderr, _("Stopped at HEAD\n"));
+       else {
+               fprintf(stderr, _("Stopped at %s\n"), message.label);
+               free_message(commit, &message);
+       }
+       return 0;
+
+}
+
 static const char rescheduled_advice[] =
 N_("Could not execute the todo command\n"
 "\n"
@@ -3332,6 +3460,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        unlink(rebase_path_stopped_sha());
                        unlink(rebase_path_amend());
                        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+
+                       if (item->command == TODO_BREAK)
+                               return stopped_at_head();
                }
                if (item->command <= TODO_SQUASH) {
                        if (is_rebase_i(opts))
@@ -3510,7 +3641,7 @@ cleanup_head_ref:
                        struct object_id orig, head;
 
                        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
-                       init_revisions(&log_tree_opt, NULL);
+                       repo_init_revisions(the_repository, &log_tree_opt, NULL);
                        log_tree_opt.diff = 1;
                        log_tree_opt.diffopt.output_format =
                                DIFF_FORMAT_DIFFSTAT;
@@ -4254,7 +4385,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;
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        revs.verbose_header = 1;
        if (!rebase_merges)
                revs.max_parents = 1;
@@ -4420,24 +4551,20 @@ int transform_todos(unsigned flags)
        return i;
 }
 
-enum check_level {
-       CHECK_IGNORE = 0, CHECK_WARN, CHECK_ERROR
-};
-
-static enum check_level get_missing_commit_check_level(void)
+enum missing_commit_check_level get_missing_commit_check_level(void)
 {
        const char *value;
 
        if (git_config_get_value("rebase.missingcommitscheck", &value) ||
                        !strcasecmp("ignore", value))
-               return CHECK_IGNORE;
+               return MISSING_COMMIT_CHECK_IGNORE;
        if (!strcasecmp("warn", value))
-               return CHECK_WARN;
+               return MISSING_COMMIT_CHECK_WARN;
        if (!strcasecmp("error", value))
-               return CHECK_ERROR;
+               return MISSING_COMMIT_CHECK_ERROR;
        warning(_("unrecognized setting %s for option "
                  "rebase.missingCommitsCheck. Ignoring."), value);
-       return CHECK_IGNORE;
+       return MISSING_COMMIT_CHECK_IGNORE;
 }
 
 define_commit_slab(commit_seen, unsigned char);
@@ -4449,7 +4576,7 @@ define_commit_slab(commit_seen, unsigned char);
  */
 int check_todo_list(void)
 {
-       enum check_level check_level = get_missing_commit_check_level();
+       enum missing_commit_check_level check_level = get_missing_commit_check_level();
        struct strbuf todo_file = STRBUF_INIT;
        struct todo_list todo_list = TODO_LIST_INIT;
        struct strbuf missing = STRBUF_INIT;
@@ -4466,7 +4593,7 @@ int check_todo_list(void)
        advise_to_edit_todo = res =
                parse_insn_buffer(todo_list.buf.buf, &todo_list);
 
-       if (res || check_level == CHECK_IGNORE)
+       if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
                goto leave_check;
 
        /* Mark the commits in git-rebase-todo as seen */
@@ -4501,7 +4628,7 @@ int check_todo_list(void)
        if (!missing.len)
                goto leave_check;
 
-       if (check_level == CHECK_ERROR)
+       if (check_level == MISSING_COMMIT_CHECK_ERROR)
                advise_to_edit_todo = res = 1;
 
        fprintf(stderr,
@@ -4547,17 +4674,17 @@ static int rewrite_file(const char *path, const char *buf, size_t len)
 }
 
 /* skip picking commits whose parents are unchanged */
-int skip_unnecessary_picks(void)
+static int skip_unnecessary_picks(struct object_id *output_oid)
 {
        const char *todo_file = rebase_path_todo();
        struct strbuf buf = STRBUF_INIT;
        struct todo_list todo_list = TODO_LIST_INIT;
-       struct object_id onto_oid, *oid = &onto_oid, *parent_oid;
+       struct object_id *parent_oid;
        int fd, i;
 
        if (!read_oneliner(&buf, rebase_path_onto(), 0))
                return error(_("could not read 'onto'"));
-       if (get_oid(buf.buf, &onto_oid)) {
+       if (get_oid(buf.buf, output_oid)) {
                strbuf_release(&buf);
                return error(_("need a HEAD to fixup"));
        }
@@ -4587,9 +4714,9 @@ int skip_unnecessary_picks(void)
                if (item->commit->parents->next)
                        break; /* merge commit */
                parent_oid = &item->commit->parents->item->object.oid;
-               if (!oideq(parent_oid, oid))
+               if (!oideq(parent_oid, output_oid))
                        break;
-               oid = &item->commit->object.oid;
+               oidcpy(output_oid, &item->commit->object.oid);
        }
        if (i > 0) {
                int offset = get_item_line_offset(&todo_list, i);
@@ -4618,15 +4745,114 @@ int skip_unnecessary_picks(void)
 
                todo_list.current = i;
                if (is_fixup(peek_command(&todo_list, 0)))
-                       record_in_rewritten(oid, peek_command(&todo_list, 0));
+                       record_in_rewritten(output_oid, peek_command(&todo_list, 0));
        }
 
        todo_list_release(&todo_list);
-       printf("%s\n", oid_to_hex(oid));
 
        return 0;
 }
 
+int complete_action(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)
+{
+       const char *shortonto, *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       struct strbuf *buf = &(todo_list.buf);
+       struct object_id oid;
+       struct stat st;
+
+       get_oid(onto, &oid);
+       shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
+
+       if (!lstat(todo_file, &st) && st.st_size == 0 &&
+           write_message("noop\n", 5, todo_file, 0))
+               return -1;
+
+       if (autosquash && rearrange_squash())
+               return -1;
+
+       if (cmd && *cmd)
+               sequencer_add_exec_commands(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)) {
+               todo_list_release(&todo_list);
+               return error(_("unusable todo list: '%s'"), todo_file);
+       }
+
+       if (count_commands(&todo_list) == 0) {
+               apply_autostash(opts);
+               sequencer_remove_state(opts);
+               todo_list_release(&todo_list);
+
+               return error(_("nothing to do"));
+       }
+
+       strbuf_addch(buf, '\n');
+       strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
+                                     "Rebase %s onto %s (%d commands)",
+                                     count_commands(&todo_list)),
+                             shortrevisions, shortonto, count_commands(&todo_list));
+       append_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);
+
+       if (write_message(buf->buf, buf->len, todo_file, 0)) {
+               todo_list_release(&todo_list);
+               return -1;
+       }
+
+       if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
+               return error(_("could not copy '%s' to '%s'."), todo_file,
+                            rebase_path_todo_backup());
+
+       if (transform_todos(flags | TODO_LIST_SHORTEN_IDS))
+               return error(_("could not transform the todo list"));
+
+       strbuf_reset(buf);
+
+       if (launch_sequence_editor(todo_file, buf, NULL)) {
+               apply_autostash(opts);
+               sequencer_remove_state(opts);
+               todo_list_release(&todo_list);
+
+               return -1;
+       }
+
+       strbuf_stripspace(buf, 1);
+       if (buf->len == 0) {
+               apply_autostash(opts);
+               sequencer_remove_state(opts);
+               todo_list_release(&todo_list);
+
+               return error(_("nothing to do"));
+       }
+
+       todo_list_release(&todo_list);
+
+       if (check_todo_list()) {
+               checkout_onto(opts, onto_name, onto, orig_head);
+               return -1;
+       }
+
+       if (transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS)))
+               return error(_("could not transform the todo list"));
+
+       if (opts->allow_ff && skip_unnecessary_picks(&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))
+               return -1;
+
+       return sequencer_continue(opts);
+}
+
 struct subject2item_entry {
        struct hashmap_entry entry;
        int i;
index c986bc825161f1f4702a0cd435c6d9705e3be2df..660cff5050b39e38e721182861ada83e95e8378b 100644 (file)
@@ -8,6 +8,7 @@ struct commit;
 
 const char *git_path_commit_editmsg(void);
 const char *git_path_seq_dir(void);
+const char *rebase_path_todo(void);
 
 #define APPEND_SIGNOFF_DEDUP (1u << 0)
 
@@ -62,6 +63,15 @@ struct replay_opts {
 };
 #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }
 
+enum missing_commit_check_level {
+       MISSING_COMMIT_CHECK_IGNORE = 0,
+       MISSING_COMMIT_CHECK_WARN,
+       MISSING_COMMIT_CHECK_ERROR
+};
+
+int write_message(const void *buf, size_t len, const char *filename,
+                 int append_eol);
+
 /* 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);
@@ -84,8 +94,12 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,
 
 int sequencer_add_exec_commands(const char *command);
 int transform_todos(unsigned flags);
+enum missing_commit_check_level get_missing_commit_check_level(void);
 int check_todo_list(void);
-int skip_unnecessary_picks(void);
+int complete_action(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);
 
 extern const char sign_off_header[];
@@ -110,8 +124,14 @@ int update_head_with_reflog(const struct commit *old_head,
 void commit_post_rewrite(const struct commit *current_head,
                         const struct object_id *new_head);
 
+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,
                          unsigned int flags);
 #endif
+
+void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
+int write_basic_state(struct replay_opts *opts, const char *head_name,
+                     const char *onto, const char *orig_head);
index a4367b8f044c6e9254d2a253149187bdb2be6dd2..dd0b6aa873a95433906b52abf605a43acc924d4a 100644 (file)
@@ -1813,7 +1813,8 @@ static void check_tag(const void *buf, size_t size)
                die(_("corrupt tag"));
 }
 
-static int index_mem(struct object_id *oid, void *buf, size_t size,
+static int index_mem(struct index_state *istate,
+                    struct object_id *oid, void *buf, size_t size,
                     enum object_type type,
                     const char *path, unsigned flags)
 {
@@ -1828,7 +1829,7 @@ static int index_mem(struct object_id *oid, void *buf, size_t size,
         */
        if ((type == OBJ_BLOB) && path) {
                struct strbuf nbuf = STRBUF_INIT;
-               if (convert_to_git(&the_index, path, buf, size, &nbuf,
+               if (convert_to_git(istate, path, buf, size, &nbuf,
                                   get_conv_flags(flags))) {
                        buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
@@ -1852,17 +1853,20 @@ static int index_mem(struct object_id *oid, void *buf, size_t size,
        return ret;
 }
 
-static int index_stream_convert_blob(struct object_id *oid, int fd,
-                                    const char *path, unsigned flags)
+static int index_stream_convert_blob(struct index_state *istate,
+                                    struct object_id *oid,
+                                    int fd,
+                                    const char *path,
+                                    unsigned flags)
 {
        int ret;
        const int write_object = flags & HASH_WRITE_OBJECT;
        struct strbuf sbuf = STRBUF_INIT;
 
        assert(path);
-       assert(would_convert_to_git_filter_fd(&the_index, path));
+       assert(would_convert_to_git_filter_fd(istate, path));
 
-       convert_to_git_filter_fd(&the_index, path, fd, &sbuf,
+       convert_to_git_filter_fd(istate, path, fd, &sbuf,
                                 get_conv_flags(flags));
 
        if (write_object)
@@ -1875,14 +1879,15 @@ static int index_stream_convert_blob(struct object_id *oid, int fd,
        return ret;
 }
 
-static int index_pipe(struct object_id *oid, int fd, enum object_type type,
+static int index_pipe(struct index_state *istate, struct object_id *oid,
+                     int fd, enum object_type type,
                      const char *path, unsigned flags)
 {
        struct strbuf sbuf = STRBUF_INIT;
        int ret;
 
        if (strbuf_read(&sbuf, fd, 4096) >= 0)
-               ret = index_mem(oid, sbuf.buf, sbuf.len, type, path, flags);
+               ret = index_mem(istate, oid, sbuf.buf, sbuf.len, type, path, flags);
        else
                ret = -1;
        strbuf_release(&sbuf);
@@ -1891,14 +1896,15 @@ static int index_pipe(struct object_id *oid, int fd, enum object_type type,
 
 #define SMALL_FILE_SIZE (32*1024)
 
-static int index_core(struct object_id *oid, int fd, size_t size,
+static int index_core(struct index_state *istate,
+                     struct object_id *oid, int fd, size_t size,
                      enum object_type type, const char *path,
                      unsigned flags)
 {
        int ret;
 
        if (!size) {
-               ret = index_mem(oid, "", size, type, path, flags);
+               ret = index_mem(istate, oid, "", size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                ssize_t read_result = read_in_full(fd, buf, size);
@@ -1909,11 +1915,11 @@ static int index_core(struct object_id *oid, int fd, size_t size,
                        ret = error(_("short read while indexing %s"),
                                    path ? path : "<unknown>");
                else
-                       ret = index_mem(oid, buf, size, type, path, flags);
+                       ret = index_mem(istate, oid, buf, size, type, path, flags);
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-               ret = index_mem(oid, buf, size, type, path, flags);
+               ret = index_mem(istate, oid, buf, size, type, path, flags);
                munmap(buf, size);
        }
        return ret;
@@ -1941,7 +1947,8 @@ static int index_stream(struct object_id *oid, int fd, size_t size,
        return index_bulk_checkin(oid, fd, size, type, path, flags);
 }
 
-int index_fd(struct object_id *oid, int fd, struct stat *st,
+int index_fd(struct index_state *istate, struct object_id *oid,
+            int fd, struct stat *st,
             enum object_type type, const char *path, unsigned flags)
 {
        int ret;
@@ -1950,14 +1957,14 @@ int index_fd(struct object_id *oid, int fd, struct stat *st,
         * Call xsize_t() only when needed to avoid potentially unnecessary
         * die() for large files.
         */
-       if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(&the_index, path))
-               ret = index_stream_convert_blob(oid, fd, path, flags);
+       if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(istate, path))
+               ret = index_stream_convert_blob(istate, oid, fd, path, flags);
        else if (!S_ISREG(st->st_mode))
-               ret = index_pipe(oid, fd, type, path, flags);
+               ret = index_pipe(istate, oid, fd, type, path, flags);
        else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
-                (path && would_convert_to_git(&the_index, path)))
-               ret = index_core(oid, fd, xsize_t(st->st_size), type, path,
-                                flags);
+                (path && would_convert_to_git(istate, path)))
+               ret = index_core(istate, oid, fd, xsize_t(st->st_size),
+                                type, path, flags);
        else
                ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
                                   flags);
@@ -1965,7 +1972,8 @@ int index_fd(struct object_id *oid, int fd, struct stat *st,
        return ret;
 }
 
-int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags)
+int index_path(struct index_state *istate, struct object_id *oid,
+              const char *path, struct stat *st, unsigned flags)
 {
        int fd;
        struct strbuf sb = STRBUF_INIT;
@@ -1976,7 +1984,7 @@ int index_path(struct object_id *oid, const char *path, struct stat *st, unsigne
                fd = open(path, O_RDONLY);
                if (fd < 0)
                        return error_errno("open(\"%s\")", path);
-               if (index_fd(oid, fd, st, OBJ_BLOB, path, flags) < 0)
+               if (index_fd(istate, oid, fd, st, OBJ_BLOB, path, flags) < 0)
                        return error(_("%s: failed to insert into database"),
                                     path);
                break;
index 99fd2d1ba0f70aaf238f55aae006a12e38090342..02fdbfc554c462c1eecdf1ddc6d17edbc1d8d853 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -185,7 +185,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
 
        is_repository_shallow(the_repository); /* make sure shallows are read */
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
        save_commit_buffer = 0;
        setup_revisions(ac, av, &revs, NULL);
 
@@ -247,6 +247,7 @@ static void check_shallow_file_for_update(struct repository *r)
 
 #define SEEN_ONLY 1
 #define VERBOSE   2
+#define QUICK 4
 
 struct write_shallow_data {
        struct strbuf *out;
@@ -261,7 +262,10 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
        const char *hex = oid_to_hex(&graft->oid);
        if (graft->nr_parent != -1)
                return 0;
-       if (data->flags & SEEN_ONLY) {
+       if (data->flags & QUICK) {
+               if (!has_object_file(&graft->oid))
+                       return 0;
+       } else if (data->flags & SEEN_ONLY) {
                struct commit *c = lookup_commit(the_repository, &graft->oid);
                if (!c || !(c->object.flags & SEEN)) {
                        if (data->flags & VERBOSE)
@@ -371,16 +375,23 @@ void advertise_shallow_grafts(int fd)
 
 /*
  * mark_reachable_objects() should have been run prior to this and all
- * reachable commits marked as "SEEN".
+ * reachable commits marked as "SEEN", except when quick_prune is non-zero,
+ * in which case lines are excised from the shallow file if they refer to
+ * commits that do not exist (any longer).
  */
-void prune_shallow(int show_only)
+void prune_shallow(unsigned options)
 {
        struct lock_file shallow_lock = LOCK_INIT;
        struct strbuf sb = STRBUF_INIT;
+       unsigned flags = SEEN_ONLY;
        int fd;
 
-       if (show_only) {
-               write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY | VERBOSE);
+       if (options & PRUNE_QUICK)
+               flags |= QUICK;
+
+       if (options & PRUNE_SHOW_ONLY) {
+               flags |= VERBOSE;
+               write_shallow_commits_1(&sb, 0, NULL, flags);
                strbuf_release(&sb);
                return;
        }
@@ -388,7 +399,7 @@ void prune_shallow(int show_only)
                                       git_path_shallow(the_repository),
                                       LOCK_DIE_ON_ERROR);
        check_shallow_file_for_update(the_repository);
-       if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
+       if (write_shallow_commits_1(&sb, 0, NULL, flags)) {
                if (write_in_full(fd, sb.buf, sb.len) < 0)
                        die_errno("failed to write to %s",
                                  get_lock_file_path(&shallow_lock));
index 84f067e10d2cec1d77ec1a51f0f38cc5a29dd89f..5820412dc5203032d1247575d79c6107a163295a 100644 (file)
@@ -111,7 +111,7 @@ static void mark_entry_for_delete(size_t pos, void *data)
                die("position for delete %d exceeds base index size %d",
                    (int)pos, istate->cache_nr);
        istate->cache[pos]->ce_flags |= CE_REMOVE;
-       istate->split_index->nr_deletions = 1;
+       istate->split_index->nr_deletions++;
 }
 
 static void replace_entry(size_t pos, void *data)
@@ -188,6 +188,30 @@ void merge_base_index(struct index_state *istate)
        si->saved_cache_nr = 0;
 }
 
+/*
+ * Compare most of the fields in two cache entries, i.e. all except the
+ * hashmap_entry and the name.
+ */
+static int compare_ce_content(struct cache_entry *a, struct cache_entry *b)
+{
+       const unsigned int ondisk_flags = CE_STAGEMASK | CE_VALID |
+                                         CE_EXTENDED_FLAGS;
+       unsigned int ce_flags = a->ce_flags;
+       unsigned int base_flags = b->ce_flags;
+       int ret;
+
+       /* only on-disk flags matter */
+       a->ce_flags &= ondisk_flags;
+       b->ce_flags &= ondisk_flags;
+       ret = memcmp(&a->ce_stat_data, &b->ce_stat_data,
+                    offsetof(struct cache_entry, name) -
+                    offsetof(struct cache_entry, ce_stat_data));
+       a->ce_flags = ce_flags;
+       b->ce_flags = base_flags;
+
+       return ret;
+}
+
 void prepare_to_write_split_index(struct index_state *istate)
 {
        struct split_index *si = init_split_index(istate);
@@ -207,38 +231,109 @@ void prepare_to_write_split_index(struct index_state *istate)
                 */
                for (i = 0; i < istate->cache_nr; i++) {
                        struct cache_entry *base;
-                       /* namelen is checked separately */
-                       const unsigned int ondisk_flags =
-                               CE_STAGEMASK | CE_VALID | CE_EXTENDED_FLAGS;
-                       unsigned int ce_flags, base_flags, ret;
                        ce = istate->cache[i];
-                       if (!ce->index)
+                       if (!ce->index) {
+                               /*
+                                * During simple update index operations this
+                                * is a cache entry that is not present in
+                                * the shared index.  It will be added to the
+                                * split index.
+                                *
+                                * However, it might also represent a file
+                                * that already has a cache entry in the
+                                * shared index, but a new index has just
+                                * been constructed by unpack_trees(), and
+                                * this entry now refers to different content
+                                * than what was recorded in the original
+                                * index, e.g. during 'read-tree -m HEAD^' or
+                                * 'checkout HEAD^'.  In this case the
+                                * original entry in the shared index will be
+                                * marked as deleted, and this entry will be
+                                * added to the split index.
+                                */
                                continue;
+                       }
                        if (ce->index > si->base->cache_nr) {
-                               ce->index = 0;
-                               continue;
+                               BUG("ce refers to a shared ce at %d, which is beyond the shared index size %d",
+                                   ce->index, si->base->cache_nr);
                        }
                        ce->ce_flags |= CE_MATCHED; /* or "shared" */
                        base = si->base->cache[ce->index - 1];
-                       if (ce == base)
+                       if (ce == base) {
+                               /* The entry is present in the shared index. */
+                               if (ce->ce_flags & CE_UPDATE_IN_BASE) {
+                                       /*
+                                        * Already marked for inclusion in
+                                        * the split index, either because
+                                        * the corresponding file was
+                                        * modified and the cached stat data
+                                        * was refreshed, or because there
+                                        * is already a replacement entry in
+                                        * the split index.
+                                        * Nothing more to do here.
+                                        */
+                               } else if (!ce_uptodate(ce) &&
+                                          is_racy_timestamp(istate, ce)) {
+                                       /*
+                                        * A racily clean cache entry stored
+                                        * only in the shared index: it must
+                                        * be added to the split index, so
+                                        * the subsequent do_write_index()
+                                        * can smudge its stat data.
+                                        */
+                                       ce->ce_flags |= CE_UPDATE_IN_BASE;
+                               } else {
+                                       /*
+                                        * The entry is only present in the
+                                        * shared index and it was not
+                                        * refreshed.
+                                        * Just leave it there.
+                                        */
+                               }
                                continue;
+                       }
                        if (ce->ce_namelen != base->ce_namelen ||
                            strcmp(ce->name, base->name)) {
                                ce->index = 0;
                                continue;
                        }
-                       ce_flags = ce->ce_flags;
-                       base_flags = base->ce_flags;
-                       /* only on-disk flags matter */
-                       ce->ce_flags   &= ondisk_flags;
-                       base->ce_flags &= ondisk_flags;
-                       ret = memcmp(&ce->ce_stat_data, &base->ce_stat_data,
-                                    offsetof(struct cache_entry, name) -
-                                    offsetof(struct cache_entry, ce_stat_data));
-                       ce->ce_flags = ce_flags;
-                       base->ce_flags = base_flags;
-                       if (ret)
+                       /*
+                        * This is the copy of a cache entry that is present
+                        * in the shared index, created by unpack_trees()
+                        * while it constructed a new index.
+                        */
+                       if (ce->ce_flags & CE_UPDATE_IN_BASE) {
+                               /*
+                                * Already marked for inclusion in the split
+                                * index, either because the corresponding
+                                * file was modified and the cached stat data
+                                * was refreshed, or because the original
+                                * entry already had a replacement entry in
+                                * the split index.
+                                * Nothing to do.
+                                */
+                       } else if (!ce_uptodate(ce) &&
+                                  is_racy_timestamp(istate, ce)) {
+                               /*
+                                * A copy of a racily clean cache entry from
+                                * the shared index.  It must be added to
+                                * the split index, so the subsequent
+                                * do_write_index() can smudge its stat data.
+                                */
                                ce->ce_flags |= CE_UPDATE_IN_BASE;
+                       } else {
+                               /*
+                                * Thoroughly compare the cached data to see
+                                * whether it should be marked for inclusion
+                                * in the split index.
+                                *
+                                * This comparison might be unnecessary, as
+                                * code paths modifying the cached data do
+                                * set CE_UPDATE_IN_BASE as well.
+                                */
+                               if (compare_ce_content(ce, base))
+                                       ce->ce_flags |= CE_UPDATE_IN_BASE;
+                       }
                        discard_cache_entry(base);
                        si->base->cache[ce->index - 1] = ce;
                }
index 64041c3c249b158478ecc6db7257519bc3fc5c19..f6a6cf78b9426abfa73a2053c17326fd645645e7 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -120,6 +120,15 @@ void strbuf_trim_trailing_dir_sep(struct strbuf *sb)
        sb->buf[sb->len] = '\0';
 }
 
+void strbuf_trim_trailing_newline(struct strbuf *sb)
+{
+       if (sb->len > 0 && sb->buf[sb->len - 1] == '\n') {
+               if (--sb->len > 0 && sb->buf[sb->len - 1] == '\r')
+                       --sb->len;
+               sb->buf[sb->len] = '\0';
+       }
+}
+
 void strbuf_ltrim(struct strbuf *sb)
 {
        char *b = sb->buf;
index 60a35aef165ca1a1bdc20b005751ea70e3c49ee0..fc40873b65124fc98a2e06150b7c8043f05ed4ab 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -87,7 +87,7 @@ struct object_id;
  * Initialize the structure. The second parameter can be zero or a bigger
  * number to allocate memory, in case you want to prevent further reallocs.
  */
-extern void strbuf_init(struct strbuf *, size_t);
+void strbuf_init(struct strbuf *sb, size_t alloc);
 
 /**
  * Release a string buffer and the memory it used. After this call, the
@@ -97,7 +97,7 @@ extern void strbuf_init(struct strbuf *, size_t);
  * To clear a strbuf in preparation for further use without the overhead
  * of free()ing and malloc()ing again, use strbuf_reset() instead.
  */
-extern void strbuf_release(struct strbuf *);
+void strbuf_release(struct strbuf *sb);
 
 /**
  * Detach the string from the strbuf and returns it; you now own the
@@ -107,7 +107,7 @@ extern void strbuf_release(struct strbuf *);
  * The strbuf that previously held the string is reset to `STRBUF_INIT` so
  * it can be reused after calling this function.
  */
-extern char *strbuf_detach(struct strbuf *, size_t *);
+char *strbuf_detach(struct strbuf *sb, size_t *sz);
 
 /**
  * Attach a string to a buffer. You should specify the string to attach,
@@ -117,7 +117,7 @@ extern char *strbuf_detach(struct strbuf *, size_t *);
  * malloc()ed, and after attaching, the pointer cannot be relied upon
  * anymore, and neither be free()d directly.
  */
-extern void strbuf_attach(struct strbuf *, void *, size_t, size_t);
+void strbuf_attach(struct strbuf *sb, void *str, size_t len, size_t mem);
 
 /**
  * Swap the contents of two string buffers.
@@ -148,7 +148,7 @@ static inline size_t strbuf_avail(const struct strbuf *sb)
  * This is never a needed operation, but can be critical for performance in
  * some cases.
  */
-extern void strbuf_grow(struct strbuf *, size_t);
+void strbuf_grow(struct strbuf *sb, size_t amount);
 
 /**
  * Set the length of the buffer to a given value. This function does *not*
@@ -183,30 +183,33 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len)
  * Strip whitespace from the beginning (`ltrim`), end (`rtrim`), or both side
  * (`trim`) of a string.
  */
-extern void strbuf_trim(struct strbuf *);
-extern void strbuf_rtrim(struct strbuf *);
-extern void strbuf_ltrim(struct strbuf *);
+void strbuf_trim(struct strbuf *sb);
+void strbuf_rtrim(struct strbuf *sb);
+void strbuf_ltrim(struct strbuf *sb);
 
 /* Strip trailing directory separators */
-extern void strbuf_trim_trailing_dir_sep(struct strbuf *);
+void strbuf_trim_trailing_dir_sep(struct strbuf *sb);
+
+/* Strip trailing LF or CR/LF */
+void strbuf_trim_trailing_newline(struct strbuf *sb);
 
 /**
  * Replace the contents of the strbuf with a reencoded form.  Returns -1
  * on error, 0 on success.
  */
-extern int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
+int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
 
 /**
  * Lowercase each character in the buffer using `tolower`.
  */
-extern void strbuf_tolower(struct strbuf *sb);
+void strbuf_tolower(struct strbuf *sb);
 
 /**
  * Compare two buffers. Returns an integer less than, equal to, or greater
  * than zero if the first buffer is found, respectively, to be less than,
  * to match, or be greater than the second buffer.
  */
-extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
+int strbuf_cmp(const struct strbuf *first, const struct strbuf *second);
 
 
 /**
@@ -233,37 +236,38 @@ static inline void strbuf_addch(struct strbuf *sb, int c)
 /**
  * Add a character the specified number of times to the buffer.
  */
-extern void strbuf_addchars(struct strbuf *sb, int c, size_t n);
+void strbuf_addchars(struct strbuf *sb, int c, size_t n);
 
 /**
  * Insert data to the given position of the buffer. The remaining contents
  * will be shifted, not overwritten.
  */
-extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t);
+void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t);
 
 /**
  * Remove given amount of data from a given position of the buffer.
  */
-extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
+void strbuf_remove(struct strbuf *sb, size_t pos, size_t len);
 
 /**
  * Remove the bytes between `pos..pos+len` and replace it with the given
  * data.
  */
-extern void strbuf_splice(struct strbuf *, size_t pos, size_t len,
-                         const void *, size_t);
+void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
+                  const void *data, size_t data_len);
 
 /**
  * Add a NUL-terminated string to the buffer. Each line will be prepended
  * by a comment character and a blank.
  */
-extern void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size);
+void strbuf_add_commented_lines(struct strbuf *out,
+                               const char *buf, size_t size);
 
 
 /**
  * Add data of given length to the buffer.
  */
-extern void strbuf_add(struct strbuf *, const void *, size_t);
+void strbuf_add(struct strbuf *sb, const void *data, size_t len);
 
 /**
  * Add a NUL-terminated string to the buffer.
@@ -282,7 +286,7 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s)
 /**
  * Copy the contents of another buffer at the end of the current one.
  */
-extern void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
+void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
 
 /**
  * This function can be used to expand a format string containing
@@ -308,8 +312,13 @@ extern void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
  * parameters to the callback, `strbuf_expand()` passes a context pointer,
  * which can be used by the programmer of the callback as she sees fit.
  */
-typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
-extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
+typedef size_t (*expand_fn_t) (struct strbuf *sb,
+                              const char *placeholder,
+                              void *context);
+void strbuf_expand(struct strbuf *sb,
+                  const char *format,
+                  expand_fn_t fn,
+                  void *context);
 
 /**
  * Used as callback for `strbuf_expand()`, expects an array of
@@ -321,7 +330,9 @@ struct strbuf_expand_dict_entry {
        const char *placeholder;
        const char *value;
 };
-extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
+size_t strbuf_expand_dict_cb(struct strbuf *sb,
+                            const char *placeholder,
+                            void *context);
 
 /**
  * Append the contents of one strbuf to another, quoting any
@@ -329,29 +340,29 @@ extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
  * destination. This is useful for literal data to be fed to either
  * strbuf_expand or to the *printf family of functions.
  */
-extern void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
+void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
 
 /**
  * Append the given byte size as a human-readable string (i.e. 12.23 KiB,
  * 3.50 MiB).
  */
-extern void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
+void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
 
 /**
  * Add a formatted string to the buffer.
  */
 __attribute__((format (printf,2,3)))
-extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
+void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
 
 /**
  * Add a formatted string prepended by a comment character and a
  * blank to the buffer.
  */
 __attribute__((format (printf, 2, 3)))
-extern void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
+void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
 
 __attribute__((format (printf,2,0)))
-extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
+void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
 
 /**
  * Add the time specified by `tm`, as formatted by `strftime`.
@@ -361,9 +372,9 @@ extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
  * `suppress_tz_name`, when set, expands %Z internally to the empty
  * string rather than passing it to `strftime`.
  */
-extern void strbuf_addftime(struct strbuf *sb, const char *fmt,
-                           const struct tm *tm, int tz_offset,
-                           int suppress_tz_name);
+void strbuf_addftime(struct strbuf *sb, const char *fmt,
+                   const struct tm *tm, int tz_offset,
+                   int suppress_tz_name);
 
 /**
  * Read a given size of data from a FILE* pointer to the buffer.
@@ -373,14 +384,14 @@ extern void strbuf_addftime(struct strbuf *sb, const char *fmt,
  * `strbuf_read()`, `strbuf_read_file()` and `strbuf_getline_*()`
  * family of functions have the same behaviour as well.
  */
-extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
+size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *file);
 
 /**
  * Read the contents of a given file descriptor. The third argument can be
  * used to give a hint about the file size, to avoid reallocs.  If read fails,
  * any partial read is undone.
  */
-extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
+ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint);
 
 /**
  * Read the contents of a given file descriptor partially by using only one
@@ -388,7 +399,7 @@ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
  * file size, to avoid reallocs. Returns the number of new bytes appended to
  * the sb.
  */
-extern ssize_t strbuf_read_once(struct strbuf *, int fd, size_t hint);
+ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint);
 
 /**
  * Read the contents of a file, specified by its path. The third argument
@@ -396,19 +407,19 @@ extern ssize_t strbuf_read_once(struct strbuf *, int fd, size_t hint);
  * Return the number of bytes read or a negative value if some error
  * occurred while opening or reading the file.
  */
-extern ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
+ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
 
 /**
  * Read the target of a symbolic link, specified by its path.  The third
  * argument can be used to give a hint about the size, to avoid reallocs.
  */
-extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
+int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
 
 /**
  * Write the whole content of the strbuf to the stream not stopping at
  * NUL bytes.
  */
-extern ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
+ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
 
 /**
  * Read a line from a FILE *, overwriting the existing contents of
@@ -422,10 +433,10 @@ extern ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
 typedef int (*strbuf_getline_fn)(struct strbuf *, FILE *);
 
 /* Uses LF as the line terminator */
-extern int strbuf_getline_lf(struct strbuf *sb, FILE *fp);
+int strbuf_getline_lf(struct strbuf *sb, FILE *fp);
 
 /* Uses NUL as the line terminator */
-extern int strbuf_getline_nul(struct strbuf *sb, FILE *fp);
+int strbuf_getline_nul(struct strbuf *sb, FILE *fp);
 
 /*
  * Similar to strbuf_getline_lf(), but additionally treats a CR that
@@ -434,14 +445,14 @@ extern int strbuf_getline_nul(struct strbuf *sb, FILE *fp);
  * that can come from platforms whose native text format is CRLF
  * terminated.
  */
-extern int strbuf_getline(struct strbuf *, FILE *);
+int strbuf_getline(struct strbuf *sb, FILE *file);
 
 
 /**
  * Like `strbuf_getline`, but keeps the trailing terminator (if
  * any) in the buffer.
  */
-extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
+int strbuf_getwholeline(struct strbuf *sb, FILE *file, int term);
 
 /**
  * Like `strbuf_getwholeline`, but operates on a file descriptor.
@@ -449,19 +460,19 @@ extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
  * use it unless you need the correct position in the file
  * descriptor.
  */
-extern int strbuf_getwholeline_fd(struct strbuf *, int, int);
+int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term);
 
 /**
  * Set the buffer to the path of the current working directory.
  */
-extern int strbuf_getcwd(struct strbuf *sb);
+int strbuf_getcwd(struct strbuf *sb);
 
 /**
  * Add a path to a buffer, converting a relative path to an
  * absolute one in the process.  Symbolic links are not
  * resolved.
  */
-extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
+void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
 
 /**
  * Canonize `path` (make it absolute, resolve symlinks, remove extra
@@ -475,7 +486,7 @@ extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
  * Callers that don't mind links should use the more lightweight
  * strbuf_add_absolute_path() instead.
  */
-extern void strbuf_add_real_path(struct strbuf *sb, const char *path);
+void strbuf_add_real_path(struct strbuf *sb, const char *path);
 
 
 /**
@@ -483,13 +494,13 @@ extern void strbuf_add_real_path(struct strbuf *sb, const char *path);
  * normalize_path_copy() for details. If an error occurs, the contents of "sb"
  * are left untouched, and -1 is returned.
  */
-extern int strbuf_normalize_path(struct strbuf *sb);
+int strbuf_normalize_path(struct strbuf *sb);
 
 /**
  * Strip whitespace from a buffer. The second parameter controls if
  * comments are considered contents to be removed or not.
  */
-extern void strbuf_stripspace(struct strbuf *buf, int skip_comments);
+void strbuf_stripspace(struct strbuf *buf, int skip_comments);
 
 static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
 {
@@ -518,8 +529,8 @@ static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
  * For lighter-weight alternatives, see string_list_split() and
  * string_list_split_in_place().
  */
-extern struct strbuf **strbuf_split_buf(const char *, size_t,
-                                       int terminator, int max);
+struct strbuf **strbuf_split_buf(const char *str, size_t len,
+                                int terminator, int max);
 
 static inline struct strbuf **strbuf_split_str(const char *str,
                                               int terminator, int max)
@@ -528,7 +539,7 @@ static inline struct strbuf **strbuf_split_str(const char *str,
 }
 
 static inline struct strbuf **strbuf_split_max(const struct strbuf *sb,
-                                               int terminator, int max)
+                                              int terminator, int max)
 {
        return strbuf_split_buf(sb->buf, sb->len, terminator, max);
 }
@@ -549,23 +560,23 @@ static inline struct strbuf **strbuf_split(const struct strbuf *sb,
  *   'element1, element2, ..., elementN'
  * to str.  If only one element, just write "element1" to str.
  */
-extern void strbuf_add_separated_string_list(struct strbuf *str,
-                                            const char *sep,
-                                            struct string_list *slist);
+void strbuf_add_separated_string_list(struct strbuf *str,
+                                     const char *sep,
+                                     struct string_list *slist);
 
 /**
  * Free a NULL-terminated list of strbufs (for example, the return
  * values of the strbuf_split*() functions).
  */
-extern void strbuf_list_free(struct strbuf **);
+void strbuf_list_free(struct strbuf **list);
 
 /**
  * Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to
  * the strbuf `sb`.
  */
-extern void strbuf_add_unique_abbrev(struct strbuf *sb,
-                                    const struct object_id *oid,
-                                    int abbrev_len);
+void strbuf_add_unique_abbrev(struct strbuf *sb,
+                             const struct object_id *oid,
+                             int abbrev_len);
 
 /**
  * Launch the user preferred editor to edit a file and fill the buffer
@@ -574,15 +585,23 @@ extern void strbuf_add_unique_abbrev(struct strbuf *sb,
  * run in. If the buffer is NULL the editor is launched as usual but the
  * file's contents are not read into the buffer upon completion.
  */
-extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
+int launch_editor(const char *path, struct strbuf *buffer,
+                 const char *const *env);
+
+int launch_sequence_editor(const char *path, struct strbuf *buffer,
+                          const char *const *env);
 
-extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size);
+void strbuf_add_lines(struct strbuf *sb,
+                     const char *prefix,
+                     const char *buf,
+                     size_t size);
 
 /**
  * Append s to sb, with the characters '<', '>', '&' and '"' converted
  * into XML entities.
  */
-extern void strbuf_addstr_xml_quoted(struct strbuf *sb, const char *s);
+void strbuf_addstr_xml_quoted(struct strbuf *sb,
+                             const char *s);
 
 /**
  * "Complete" the contents of `sb` by ensuring that either it ends with the
@@ -612,8 +631,8 @@ static inline void strbuf_complete_line(struct strbuf *sb)
  * If "allowed" is non-zero, restrict the set of allowed expansions. See
  * interpret_branch_name() for details.
  */
-extern void strbuf_branchname(struct strbuf *sb, const char *name,
-                             unsigned allowed);
+void strbuf_branchname(struct strbuf *sb, const char *name,
+                      unsigned allowed);
 
 /*
  * Like strbuf_branchname() above, but confirm that the result is
@@ -621,15 +640,15 @@ extern void strbuf_branchname(struct strbuf *sb, const char *name,
  *
  * The return value is "0" if the result is valid, and "-1" otherwise.
  */
-extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
+int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 
-extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
-                                   int reserved);
+void strbuf_addstr_urlencode(struct strbuf *sb, const char *name,
+                            int reserved);
 
 __attribute__((format (printf,1,2)))
-extern int printf_ln(const char *fmt, ...);
+int printf_ln(const char *fmt, ...);
 __attribute__((format (printf,2,3)))
-extern int fprintf_ln(FILE *fp, const char *fmt, ...);
+int fprintf_ln(FILE *fp, const char *fmt, ...);
 
 char *xstrdup_tolower(const char *);
 char *xstrdup_toupper(const char *);
index b53cb6e9c4714c3582aab35fea5bfb3739658348..d9d3046689c0aa44458d0417cae8abd343535bbb 100644 (file)
@@ -428,7 +428,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
 {
        struct commit_list *list;
 
-       init_revisions(rev, NULL);
+       repo_init_revisions(the_repository, rev, NULL);
        setup_revisions(0, NULL, rev, NULL);
        rev->left_right = 1;
        rev->first_parent_only = 1;
@@ -766,13 +766,14 @@ static void collect_changed_submodules_cb(struct diff_queue_struct *q,
  * have a corresponding 'struct oid_array' (in the 'util' field) which lists
  * what the submodule pointers were updated to during the change.
  */
-static void collect_changed_submodules(struct string_list *changed,
+static void collect_changed_submodules(struct index_state *istate,
+                                      struct string_list *changed,
                                       struct argv_array *argv)
 {
        struct rev_info rev;
        const struct commit *commit;
 
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        setup_revisions(argv->argc, argv->argv, &rev, NULL);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
@@ -783,7 +784,7 @@ static void collect_changed_submodules(struct string_list *changed,
                data.changed = changed;
                data.commit_oid = &commit->object.oid;
 
-               init_revisions(&diff_rev, NULL);
+               repo_init_revisions(the_repository, &diff_rev, NULL);
                diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
                diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
                diff_rev.diffopt.format_callback_data = &data;
@@ -930,8 +931,10 @@ static int submodule_needs_pushing(const char *path, struct oid_array *commits)
        return 0;
 }
 
-int find_unpushed_submodules(struct oid_array *commits,
-               const char *remotes_name, struct string_list *needs_pushing)
+int find_unpushed_submodules(struct index_state *istate,
+                            struct oid_array *commits,
+                            const char *remotes_name,
+                            struct string_list *needs_pushing)
 {
        struct string_list submodules = STRING_LIST_INIT_DUP;
        struct string_list_item *name;
@@ -943,7 +946,7 @@ int find_unpushed_submodules(struct oid_array *commits,
        argv_array_push(&argv, "--not");
        argv_array_pushf(&argv, "--remotes=%s", remotes_name);
 
-       collect_changed_submodules(&submodules, &argv);
+       collect_changed_submodules(istate, &submodules, &argv);
 
        for_each_string_list_item(name, &submodules) {
                struct oid_array *commits = name->util;
@@ -1044,7 +1047,8 @@ static void submodule_push_check(const char *path, const char *head,
                die("process for submodule '%s' failed", path);
 }
 
-int push_unpushed_submodules(struct oid_array *commits,
+int push_unpushed_submodules(struct index_state *istate,
+                            struct oid_array *commits,
                             const struct remote *remote,
                             const struct refspec *rs,
                             const struct string_list *push_options,
@@ -1053,7 +1057,8 @@ int push_unpushed_submodules(struct oid_array *commits,
        int i, ret = 1;
        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 
-       if (!find_unpushed_submodules(commits, remote->name, &needs_pushing))
+       if (!find_unpushed_submodules(istate, commits,
+                                     remote->name, &needs_pushing))
                return 1;
 
        /*
@@ -1110,7 +1115,7 @@ void check_for_new_submodule_commits(struct object_id *oid)
        oid_array_append(&ref_tips_after_fetch, oid);
 }
 
-static void calculate_changed_submodule_paths(void)
+static void calculate_changed_submodule_paths(struct index_state *istate)
 {
        struct argv_array argv = ARGV_ARRAY_INIT;
        struct string_list changed_submodules = STRING_LIST_INIT_DUP;
@@ -1131,7 +1136,7 @@ static void calculate_changed_submodule_paths(void)
         * Collect all submodules (whether checked out or not) for which new
         * commits have been recorded upstream in "changed_submodule_names".
         */
-       collect_changed_submodules(&changed_submodules, &argv);
+       collect_changed_submodules(istate, &changed_submodules, &argv);
 
        for_each_string_list_item(name, &changed_submodules) {
                struct oid_array *commits = name->util;
@@ -1158,7 +1163,8 @@ static void calculate_changed_submodule_paths(void)
        initialized_fetch_ref_tips = 0;
 }
 
-int submodule_touches_in_range(struct object_id *excl_oid,
+int submodule_touches_in_range(struct index_state *istate,
+                              struct object_id *excl_oid,
                               struct object_id *incl_oid)
 {
        struct string_list subs = STRING_LIST_INIT_DUP;
@@ -1176,7 +1182,7 @@ int submodule_touches_in_range(struct object_id *excl_oid,
                argv_array_push(&args, oid_to_hex(excl_oid));
        }
 
-       collect_changed_submodules(&subs, &args);
+       collect_changed_submodules(istate, &subs, &args);
        ret = subs.nr;
 
        argv_array_clear(&args);
@@ -1346,7 +1352,7 @@ 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();
+       calculate_changed_submodule_paths(r->index);
        run_processes_parallel(max_parallel_jobs,
                               get_next_submodule,
                               fetch_start_failure,
@@ -1880,7 +1886,7 @@ const char *get_superproject_working_tree(void)
                 * We're only interested in the name after the tab.
                 */
                super_sub = strchr(sb.buf, '\t') + 1;
-               super_sub_len = sb.buf + sb.len - super_sub - 1;
+               super_sub_len = strlen(super_sub);
 
                if (super_sub_len > cwd_len ||
                    strcmp(&cwd[cwd_len - super_sub_len], super_sub))
index e452919aa467413107ea749fe90b17e4e3e198c4..4826601ff2f2b53d1bc0db03779e6a151fccadc5 100644 (file)
@@ -102,13 +102,16 @@ int add_submodule_odb(const char *path);
  * Checks if there are submodule changes in a..b. If a is the null OID,
  * checks b and all its ancestors instead.
  */
-int submodule_touches_in_range(struct object_id *a,
+int submodule_touches_in_range(struct index_state *istate,
+                              struct object_id *a,
                               struct object_id *b);
-int find_unpushed_submodules(struct oid_array *commits,
+int find_unpushed_submodules(struct index_state *istate,
+                            struct oid_array *commits,
                             const char *remotes_name,
                             struct string_list *needs_pushing);
 struct refspec;
-int push_unpushed_submodules(struct oid_array *commits,
+int push_unpushed_submodules(struct index_state *istate,
+                            struct oid_array *commits,
                             const struct remote *remote,
                             const struct refspec *rs,
                             const struct string_list *push_options,
index 4d8dbc7c5f5800bb838a09a8ca87fab8a3de72ab..88474896400308fa7a865ea1fb66f9e7d38f9a13 100644 (file)
--- a/t/README
+++ b/t/README
@@ -315,7 +315,7 @@ packs on demand. This normally only happens when the object size is
 over 2GB. This variable forces the code path on any object larger than
 <n> bytes.
 
-GIT_TEST_OE_DELTA_SIZE=<n> exercises the uncomon pack-objects code
+GIT_TEST_OE_DELTA_SIZE=<n> exercises the uncommon pack-objects code
 path where deltas larger than this limit require extra memory
 allocation for bookkeeping.
 
@@ -327,6 +327,22 @@ GIT_TEST_COMMIT_GRAPH=<boolean>, when true, forces the commit-graph to
 be written after every 'git commit' command, and overrides the
 'core.commitGraph' setting to true.
 
+GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all exercises the fsmonitor
+code path for utilizing a file system monitor to speed up detecting
+new or changed files.
+
+GIT_TEST_INDEX_VERSION=<n> exercises the index read/write code path
+for the index version specified.  Can be set to any valid version
+(currently 2, 3, or 4).
+
+GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
+by overriding the minimum number of cache entries required per thread.
+
+GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
+of the index for the whole test suite by bypassing the default number of
+cache entries and thread minimums. Setting this to 1 will make the
+index loading single threaded.
+
 Naming Tests
 ------------
 
index 4f8bc758213c47906d3dc1a4d4e02df2737c508d..625b2dbf8226fee261c52b3cf74f24187e0ca68e 100644 (file)
@@ -32,7 +32,7 @@ static int run_revision_walk(void)
        int argc = ARRAY_SIZE(argv) - 1;
        int got_revision = 0;
 
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        setup_revisions(argc, argv, &rev, NULL);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
index 6b5836dc1b93b85333f2acb9cf1717a4462e209c..5df8b682aa8ab0ce305dd8f22d3b7c4780331d71 100644 (file)
@@ -55,13 +55,23 @@ static struct test_cmd cmds[] = {
        { "write-cache", cmd__write_cache },
 };
 
+static NORETURN void die_usage(void)
+{
+       size_t i;
+
+       fprintf(stderr, "usage: test-tool <toolname> [args]\n");
+       for (i = 0; i < ARRAY_SIZE(cmds); i++)
+               fprintf(stderr, "  %s\n", cmds[i].name);
+       exit(128);
+}
+
 int cmd_main(int argc, const char **argv)
 {
        int i;
 
        BUG_exit_code = 99;
        if (argc < 2)
-               die("I need a test name!");
+               die_usage();
 
        for (i = 0; i < ARRAY_SIZE(cmds); i++) {
                if (!strcmp(cmds[i].name, argv[1])) {
@@ -70,5 +80,6 @@ int cmd_main(int argc, const char **argv)
                        return cmds[i].fn(argc, argv);
                }
        }
-       die("There is no test named '%s'", argv[1]);
+       error("there is no tool named '%s'", argv[1]);
+       die_usage();
 }
index e4890566da552eb10ae718c4c921ad103c3f1a71..71f470b87141a23f881a66349c3c409207a3c7f9 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __TEST_TOOL_H__
-#define __TEST_TOOL_H__
+#ifndef TEST_TOOL_H
+#define TEST_TOOL_H
 
 #include "git-compat-util.h"
 
index 2ca9fb69d62083494869fff6420eec39834b2eb4..f6c45ee08f6e348bc7af2939d2bfa65f5c057ae0 100644 (file)
@@ -49,7 +49,7 @@ set_fake_editor () {
                case $line in
                pick|squash|fixup|edit|reword|drop)
                        action="$line";;
-               exec*)
+               exec*|break|b)
                        echo "$line" | sed 's/_/ /g' >> "$1";;
                "#")
                        echo '# comment' >> "$1";;
index 26601e698bd3abebd2e1f10ba6012f80c65ffa85..4d23373526945eea7bc4e875beac5fdc4451cac8 100755 (executable)
@@ -87,6 +87,10 @@ _run_sub_test_lib_test_common () {
                passing metrics
                '
 
+               # Tell the framework that we are self-testing to make sure
+               # it yields a stable result.
+               GIT_TEST_FRAMEWORK_SELFTEST=t &&
+
                # Point to the t/test-lib.sh, which isn't in ../ as usual
                . "\$TEST_DIRECTORY"/test-lib.sh
                EOF
index bc27df7f383d14fb5fea6266591469c32e562c4e..e8ef7300ecb8693367906d4c314797e5750bdf50 100755 (executable)
@@ -29,9 +29,9 @@ test_expect_success "setup" '
 # to verify
 test_expect_success 'basic help commands' '
        git help >/dev/null &&
-       git help -a >/dev/null &&
+       git help -a --no-verbose >/dev/null &&
        git help -g >/dev/null &&
-       git help -av >/dev/null
+       git help -a >/dev/null
 '
 
 test_expect_success "works for commands and guides by default" '
index 3e131c5325e363fa6188456e3f5f3f47f762801e..cf932c851411e5abd992005de474cc095abecead 100755 (executable)
@@ -12,10 +12,14 @@ cat >hello-script <<-EOF
        cat hello-script
 EOF
 
-test_expect_success 'start_command reports ENOENT' '
+test_expect_success 'start_command reports ENOENT (slash)' '
        test-tool run-command start-command-ENOENT ./does-not-exist
 '
 
+test_expect_success 'start_command reports ENOENT (no slash)' '
+       test-tool run-command start-command-ENOENT does-not-exist
+'
+
 test_expect_success 'run_command can run a command' '
        cat hello-script >hello.sh &&
        chmod +x hello.sh &&
@@ -25,6 +29,13 @@ test_expect_success 'run_command can run a command' '
        test_must_be_empty err
 '
 
+test_expect_success 'run_command is restricted to PATH' '
+       write_script should-not-run <<-\EOF &&
+       echo yikes
+       EOF
+       test_must_fail test-tool run-command run-command should-not-run
+'
+
 test_expect_success !MINGW 'run_command can run a script without a #! line' '
        cat >hello <<-\EOF &&
        cat hello-script
index cfd0655ea19b0caa8d55fd41b5b76abde4de1c8b..ba3887f178b03a71b1ee1d2149429e2ee76814a6 100755 (executable)
@@ -182,6 +182,47 @@ test_expect_success 'fetching of missing objects works with ref-in-want enabled'
        grep "git< fetch=.*ref-in-want" trace
 '
 
+test_expect_success 'fetching of missing blobs works' '
+       rm -rf server repo &&
+       test_create_repo server &&
+       test_commit -C server foo &&
+       git -C server repack -a -d --write-bitmap-index &&
+
+       git clone "file://$(pwd)/server" repo &&
+       git hash-object repo/foo.t >blobhash &&
+       rm -rf repo/.git/objects/* &&
+
+       git -C server config uploadpack.allowanysha1inwant 1 &&
+       git -C server config uploadpack.allowfilter 1 &&
+       git -C repo config core.repositoryformatversion 1 &&
+       git -C repo config extensions.partialclone "origin" &&
+
+       git -C repo cat-file -p $(cat blobhash)
+'
+
+test_expect_success 'fetching of missing trees does not fetch blobs' '
+       rm -rf server repo &&
+       test_create_repo server &&
+       test_commit -C server foo &&
+       git -C server repack -a -d --write-bitmap-index &&
+
+       git clone "file://$(pwd)/server" repo &&
+       git -C repo rev-parse foo^{tree} >treehash &&
+       git hash-object repo/foo.t >blobhash &&
+       rm -rf repo/.git/objects/* &&
+
+       git -C server config uploadpack.allowanysha1inwant 1 &&
+       git -C server config uploadpack.allowfilter 1 &&
+       git -C repo config core.repositoryformatversion 1 &&
+       git -C repo config extensions.partialclone "origin" &&
+       git -C repo cat-file -p $(cat treehash) &&
+
+       # Ensure that the tree, but not the blob, is fetched
+       git -C repo rev-list --objects --missing=print $(cat treehash) >objects &&
+       grep "^$(cat treehash)" objects &&
+       grep "^[?]$(cat blobhash)" objects
+'
+
 test_expect_success 'rev-list stops traversal at missing and promised commit' '
        rm -rf repo &&
        test_create_repo repo &&
@@ -198,6 +239,51 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' '
        ! grep $FOO out
 '
 
+test_expect_success 'missing tree objects with --missing=allow-promisor and --exclude-promisor-objects' '
+       rm -rf repo &&
+       test_create_repo repo &&
+       test_commit -C repo foo &&
+       test_commit -C repo bar &&
+       test_commit -C repo baz &&
+
+       promise_and_delete $(git -C repo rev-parse bar^{tree}) &&
+       promise_and_delete $(git -C repo rev-parse foo^{tree}) &&
+
+       git -C repo config core.repositoryformatversion 1 &&
+       git -C repo config extensions.partialclone "arbitrary string" &&
+
+       git -C repo rev-list --missing=allow-promisor --objects HEAD >objs 2>rev_list_err &&
+       test_must_be_empty rev_list_err &&
+       # 3 commits, 3 blobs, and 1 tree
+       test_line_count = 7 objs &&
+
+       # Do the same for --exclude-promisor-objects, but with all trees gone.
+       promise_and_delete $(git -C repo rev-parse baz^{tree}) &&
+       git -C repo rev-list --exclude-promisor-objects --objects HEAD >objs 2>rev_list_err &&
+       test_must_be_empty rev_list_err &&
+       # 3 commits, no blobs or trees
+       test_line_count = 3 objs
+'
+
+test_expect_success 'missing non-root tree object and rev-list' '
+       rm -rf repo &&
+       test_create_repo repo &&
+       mkdir repo/dir &&
+       echo foo >repo/dir/foo &&
+       git -C repo add dir/foo &&
+       git -C repo commit -m "commit dir/foo" &&
+
+       promise_and_delete $(git -C repo rev-parse HEAD:dir) &&
+
+       git -C repo config core.repositoryformatversion 1 &&
+       git -C repo config extensions.partialclone "arbitrary string" &&
+
+       git -C repo rev-list --missing=allow-any --objects HEAD >objs 2>rev_list_err &&
+       test_must_be_empty rev_list_err &&
+       # 1 commit and 1 tree
+       test_line_count = 2 objs
+'
+
 test_expect_success 'rev-list stops traversal at missing and promised tree' '
        rm -rf repo &&
        test_create_repo repo &&
index 25d7c700f6f480076f3ad00e6eeb709f8de517fa..090b7fc3d35d1a46a18b8ae385a7cc1213e6650d 100755 (executable)
@@ -63,4 +63,37 @@ test_expect_success 'return to full checkout of master' '
        test "$(cat b)" = "modified"
 '
 
+test_expect_success 'in partial clone, sparse checkout only fetches needed blobs' '
+       test_create_repo server &&
+       git clone "file://$(pwd)/server" client &&
+
+       test_config -C server uploadpack.allowfilter 1 &&
+       test_config -C server uploadpack.allowanysha1inwant 1 &&
+       echo a >server/a &&
+       echo bb >server/b &&
+       mkdir server/c &&
+       echo ccc >server/c/c &&
+       git -C server add a b c/c &&
+       git -C server commit -m message &&
+
+       test_config -C client core.sparsecheckout 1 &&
+       test_config -C client extensions.partialclone origin &&
+       echo "!/*" >client/.git/info/sparse-checkout &&
+       echo "/a" >>client/.git/info/sparse-checkout &&
+       git -C client fetch --filter=blob:none origin &&
+       git -C client checkout FETCH_HEAD &&
+
+       git -C client rev-list HEAD \
+               --quiet --objects --missing=print >unsorted_actual &&
+       (
+               printf "?" &&
+               git hash-object server/b &&
+               printf "?" &&
+               git hash-object server/c/c
+       ) >unsorted_expect &&
+       sort unsorted_actual >actual &&
+       sort unsorted_expect >expect &&
+       test_cmp expect actual
+'
+
 test_done
index 5c715fe2cf6c7afe2c39e6a7197cdd3d33329b8b..01abee533dedfd1e2d8bd347d06fc5c0c8b7833a 100755 (executable)
@@ -141,6 +141,22 @@ test_expect_success 'showing the superproject correctly' '
        test_commit -C sub test_commit &&
        git -C super submodule add ../sub dir/sub &&
        echo $(pwd)/super >expect  &&
+       git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
+       test_cmp expect out &&
+
+       test_commit -C super submodule_add &&
+       git -C super checkout -b branch1 &&
+       git -C super/dir/sub checkout -b branch1 &&
+       test_commit -C super/dir/sub branch1_commit &&
+       git -C super add dir/sub &&
+       test_commit -C super branch1_commit &&
+       git -C super checkout -b branch2 master &&
+       git -C super/dir/sub checkout -b branch2 master &&
+       test_commit -C super/dir/sub branch2_commit &&
+       git -C super add dir/sub &&
+       test_commit -C super branch2_commit &&
+       test_must_fail git -C super merge branch1 &&
+
        git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
        test_cmp expect out
 '
index be22398a8545419f3d12d4d48cb636c5273b5206..2ac47aa0e4a7164cd06037496832a1744990a774 100755 (executable)
@@ -6,7 +6,19 @@ test_description='split index mode tests'
 
 # We need total control of index splitting here
 sane_unset GIT_TEST_SPLIT_INDEX
-sane_unset GIT_FSMONITOR_TEST
+
+# Testing a hard coded SHA against an index with an extension
+# that can vary from run to run is problematic so we disable
+# those extensions.
+sane_unset GIT_TEST_FSMONITOR
+sane_unset GIT_TEST_INDEX_THREADS
+
+# Create a file named as $1 with content read from stdin.
+# Set the file's mtime to a few seconds in the past to avoid racy situations.
+create_non_racy_file () {
+       cat >"$1" &&
+       test-tool chmtime =-5 "$1"
+}
 
 test_expect_success 'enable split index' '
        git config splitIndex.maxPercentChange 100 &&
@@ -15,11 +27,11 @@ test_expect_success 'enable split index' '
        indexversion=$(test-tool index-version <.git/index) &&
        if test "$indexversion" = "4"
        then
-               own=432ef4b63f32193984f339431fd50ca796493569
-               base=508851a7f0dfa8691e9f69c7f055865389012491
+               own=3527df833c6c100d3d1d921a9a782d62a8be4b58
+               base=746f7ab2ed44fb839efdfbffcf399d0b113fb4cb
        else
-               own=8299b0bcd1ac364e5f1d7768efb62fa2da79a339
-               base=39d890139ee5356c7ef572216cebcd27aa41f9df
+               own=5e9b60117ece18da410ddecc8b8d43766a0e4204
+               base=4370042739b31cd17a5c5cd6043a77c9a00df113
        fi &&
        cat >expect <<-EOF &&
        own $own
@@ -31,7 +43,7 @@ test_expect_success 'enable split index' '
 '
 
 test_expect_success 'add one file' '
-       : >one &&
+       create_non_racy_file one &&
        git update-index --add one &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -83,7 +95,7 @@ test_expect_success 'enable split index again, "one" now belongs to base index"'
 '
 
 test_expect_success 'modify original file, base index untouched' '
-       echo modified >one &&
+       echo modified | create_non_racy_file one &&
        git update-index one &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -102,7 +114,7 @@ test_expect_success 'modify original file, base index untouched' '
 '
 
 test_expect_success 'add another file, which stays index' '
-       : >two &&
+       create_non_racy_file two &&
        git update-index --add two &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -155,7 +167,7 @@ test_expect_success 'remove file in base index' '
 '
 
 test_expect_success 'add original file back' '
-       : >one &&
+       create_non_racy_file one &&
        git update-index --add one &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -174,7 +186,7 @@ test_expect_success 'add original file back' '
 '
 
 test_expect_success 'add new file' '
-       : >two &&
+       create_non_racy_file two &&
        git update-index --add two &&
        git ls-files --stage >actual &&
        cat >expect <<-EOF &&
@@ -218,7 +230,7 @@ test_expect_success 'rev-parse --shared-index-path' '
 
 test_expect_success 'set core.splitIndex config variable to true' '
        git config core.splitIndex true &&
-       : >three &&
+       create_non_racy_file three &&
        git update-index --add three &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -253,9 +265,9 @@ test_expect_success 'set core.splitIndex config variable to false' '
        test_cmp expect actual
 '
 
-test_expect_success 'set core.splitIndex config variable to true' '
+test_expect_success 'set core.splitIndex config variable back to true' '
        git config core.splitIndex true &&
-       : >three &&
+       create_non_racy_file three &&
        git update-index --add three &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -265,7 +277,7 @@ test_expect_success 'set core.splitIndex config variable to true' '
        deletions:
        EOF
        test_cmp expect actual &&
-       : >four &&
+       create_non_racy_file four &&
        git update-index --add four &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
        cat >expect <<-EOF &&
@@ -279,7 +291,7 @@ test_expect_success 'set core.splitIndex config variable to true' '
 
 test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
        git config --unset splitIndex.maxPercentChange &&
-       : >five &&
+       create_non_racy_file five &&
        git update-index --add five &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -289,7 +301,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
        deletions:
        EOF
        test_cmp expect actual &&
-       : >six &&
+       create_non_racy_file six &&
        git update-index --add six &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
        cat >expect <<-EOF &&
@@ -303,7 +315,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
 
 test_expect_success 'check splitIndex.maxPercentChange set to 0' '
        git config splitIndex.maxPercentChange 0 &&
-       : >seven &&
+       create_non_racy_file seven &&
        git update-index --add seven &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -313,7 +325,7 @@ test_expect_success 'check splitIndex.maxPercentChange set to 0' '
        deletions:
        EOF
        test_cmp expect actual &&
-       : >eight &&
+       create_non_racy_file eight &&
        git update-index --add eight &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -326,17 +338,17 @@ test_expect_success 'check splitIndex.maxPercentChange set to 0' '
 '
 
 test_expect_success 'shared index files expire after 2 weeks by default' '
-       : >ten &&
+       create_non_racy_file ten &&
        git update-index --add ten &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        just_under_2_weeks_ago=$((5-14*86400)) &&
        test-tool chmtime =$just_under_2_weeks_ago .git/sharedindex.* &&
-       : >eleven &&
+       create_non_racy_file eleven &&
        git update-index --add eleven &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        just_over_2_weeks_ago=$((-1-14*86400)) &&
        test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
-       : >twelve &&
+       create_non_racy_file twelve &&
        git update-index --add twelve &&
        test $(ls .git/sharedindex.* | wc -l) -le 2
 '
@@ -344,12 +356,12 @@ test_expect_success 'shared index files expire after 2 weeks by default' '
 test_expect_success 'check splitIndex.sharedIndexExpire set to 16 days' '
        git config splitIndex.sharedIndexExpire "16.days.ago" &&
        test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
-       : >thirteen &&
+       create_non_racy_file thirteen &&
        git update-index --add thirteen &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        just_over_16_days_ago=$((-1-16*86400)) &&
        test-tool chmtime =$just_over_16_days_ago .git/sharedindex.* &&
-       : >fourteen &&
+       create_non_racy_file fourteen &&
        git update-index --add fourteen &&
        test $(ls .git/sharedindex.* | wc -l) -le 2
 '
@@ -358,13 +370,13 @@ test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now"
        git config splitIndex.sharedIndexExpire never &&
        just_10_years_ago=$((-365*10*86400)) &&
        test-tool chmtime =$just_10_years_ago .git/sharedindex.* &&
-       : >fifteen &&
+       create_non_racy_file fifteen &&
        git update-index --add fifteen &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        git config splitIndex.sharedIndexExpire now &&
        just_1_second_ago=-1 &&
        test-tool chmtime =$just_1_second_ago .git/sharedindex.* &&
-       : >sixteen &&
+       create_non_racy_file sixteen &&
        git update-index --add sixteen &&
        test $(ls .git/sharedindex.* | wc -l) -le 2
 '
@@ -379,7 +391,7 @@ do
                # Create one new shared index file
                git config core.sharedrepository "$mode" &&
                git config core.splitIndex true &&
-               : >one &&
+               create_non_racy_file one &&
                git update-index --add one &&
                echo "$modebits" >expect &&
                test_modebits .git/index >actual &&
diff --git a/t/t1701-racy-split-index.sh b/t/t1701-racy-split-index.sh
new file mode 100755 (executable)
index 0000000..5dc221e
--- /dev/null
@@ -0,0 +1,214 @@
+#!/bin/sh
+
+# This test can give false success if your machine is sufficiently
+# slow or all trials happened to happen on second boundaries.
+
+test_description='racy split index'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       # Only split the index when the test explicitly says so.
+       sane_unset GIT_TEST_SPLIT_INDEX &&
+       git config splitIndex.maxPercentChange 100 &&
+
+       echo "cached content" >racy-file &&
+       git add racy-file &&
+       git commit -m initial &&
+
+       echo something >other-file &&
+       # No raciness with this file.
+       test-tool chmtime =-20 other-file &&
+
+       echo "+cached content" >expect
+'
+
+check_cached_diff () {
+       git diff-index --patch --cached $EMPTY_TREE racy-file >diff &&
+       tail -1 diff >actual &&
+       test_cmp expect actual
+}
+
+trials="0 1 2 3 4"
+for trial in $trials
+do
+       test_expect_success "split the index while adding a racily clean file #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second (so both writes to racy-file result in the same
+               # mtime) to create the interesting racy situation.
+               echo "cached content" >racy-file &&
+
+               # Update and split the index.  The cache entry of
+               # racy-file will be stored only in the shared index.
+               git update-index --split-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Subsequent git commands should notice that racy-file
+               # and the split index have the same mtime, and check
+               # the content of the file to see if it is actually
+               # clean.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "add a racily clean file to an already split index #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               git update-index --split-index &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update the split index.  The cache entry of racy-file
+               # will be stored only in the split index.
+               git update-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Subsequent git commands should notice that racy-file
+               # and the split index have the same mtime, and check
+               # the content of the file to see if it is actually
+               # clean.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "split the index when the index contains a racily clean cache entry #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               git update-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update and split the index when the index contains
+               # the racily clean cache entry of racy-file.
+               # A corresponding replacement cache entry with smudged
+               # stat data should be added to the new split index.
+               git update-index --split-index --add other-file &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data in the replacement cache entry and that it
+               # doesnt match with the file the worktree.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "update the split index when it contains a new racily clean cache entry #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               git update-index --split-index &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update the split index.  The cache entry of racy-file
+               # will be stored only in the split index.
+               git update-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update the split index when the racily clean cache
+               # entry of racy-file is only stored in the split index.
+               # An updated cache entry with smudged stat data should
+               # be added to the new split index.
+               git update-index --add other-file &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "update the split index when a racily clean cache entry is stored only in the shared index #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update and split the index.  The cache entry of
+               # racy-file will be stored only in the shared index.
+               git update-index --split-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update the split index when the racily clean cache
+               # entry of racy-file is only stored in the shared index.
+               # A corresponding replacement cache entry with smudged
+               # stat data should be added to the new split index.
+               git update-index --add other-file &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "update the split index after unpack trees() copied a racily clean cache entry from the shared index #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update and split the index.  The cache entry of
+               # racy-file will be stored only in the shared index.
+               git update-index --split-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update the split index after unpack_trees() copied the
+               # racily clean cache entry of racy-file from the shared
+               # index.  A corresponding replacement cache entry
+               # with smudged stat data should be added to the new
+               # split index.
+               git read-tree -m HEAD &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data.
+               check_cached_diff
+       '
+done
+
+test_done
diff --git a/t/t2000-checkout-cache-clash.sh b/t/t2000-checkout-cache-clash.sh
deleted file mode 100755 (executable)
index de3edb5..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-
-test_description='git checkout-index test.
-
-This test registers the following filesystem structure in the
-cache:
-
-    path0       - a file
-    path1/file1 - a file in a directory
-
-And then tries to checkout in a work tree that has the following:
-
-    path0/file0 - a file in a directory
-    path1       - a file
-
-The git checkout-index command should fail when attempting to checkout
-path0, finding it is occupied by a directory, and path1/file1, finding
-path1 is occupied by a non-directory.  With "-f" flag, it should remove
-the conflicting paths and succeed.
-'
-. ./test-lib.sh
-
-date >path0
-mkdir path1
-date >path1/file1
-
-test_expect_success \
-    'git update-index --add various paths.' \
-    'git update-index --add path0 path1/file1'
-
-rm -fr path0 path1
-mkdir path0
-date >path0/file0
-date >path1
-
-test_expect_success \
-    'git checkout-index without -f should fail on conflicting work tree.' \
-    'test_must_fail git checkout-index -a'
-
-test_expect_success \
-    'git checkout-index with -f should succeed.' \
-    'git checkout-index -f -a'
-
-test_expect_success \
-    'git checkout-index conflicting paths.' \
-    'test -f path0 && test -d path1 && test -f path1/file1'
-
-test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' '
-       mkdir -p tar/get &&
-       ln -s tar/get there &&
-       echo first &&
-       git checkout-index -a -f --prefix=there/ &&
-       echo second &&
-       git checkout-index -a -f --prefix=there/
-'
-
-test_done
diff --git a/t/t2000-conflict-when-checking-files-out.sh b/t/t2000-conflict-when-checking-files-out.sh
new file mode 100755 (executable)
index 0000000..f18616a
--- /dev/null
@@ -0,0 +1,135 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git conflicts when checking files out test.'
+
+# The first test registers the following filesystem structure in the
+# cache:
+#
+#     path0       - a file
+#     path1/file1 - a file in a directory
+#
+# And then tries to checkout in a work tree that has the following:
+#
+#     path0/file0 - a file in a directory
+#     path1       - a file
+#
+# The git checkout-index command should fail when attempting to checkout
+# path0, finding it is occupied by a directory, and path1/file1, finding
+# path1 is occupied by a non-directory.  With "-f" flag, it should remove
+# the conflicting paths and succeed.
+
+. ./test-lib.sh
+
+show_files() {
+       # show filesystem files, just [-dl] for type and name
+       find path? -ls |
+       sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /'
+       # what's in the cache, just mode and name
+       git ls-files --stage |
+       sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /'
+       # what's in the tree, just mode and name.
+       git ls-tree -r "$1" |
+       sed -e 's/^\([0-9]*\)   [^ ]*   [0-9a-f]*       /tr: \1 /'
+}
+
+date >path0
+mkdir path1
+date >path1/file1
+
+test_expect_success \
+    'git update-index --add various paths.' \
+    'git update-index --add path0 path1/file1'
+
+rm -fr path0 path1
+mkdir path0
+date >path0/file0
+date >path1
+
+test_expect_success \
+    'git checkout-index without -f should fail on conflicting work tree.' \
+    'test_must_fail git checkout-index -a'
+
+test_expect_success \
+    'git checkout-index with -f should succeed.' \
+    'git checkout-index -f -a'
+
+test_expect_success \
+    'git checkout-index conflicting paths.' \
+    'test -f path0 && test -d path1 && test -f path1/file1'
+
+test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' '
+       mkdir -p tar/get &&
+       ln -s tar/get there &&
+       echo first &&
+       git checkout-index -a -f --prefix=there/ &&
+       echo second &&
+       git checkout-index -a -f --prefix=there/
+'
+
+# The second test registers the following filesystem structure in the cache:
+#
+#     path2/file0      - a file in a directory
+#     path3/file1 - a file in a directory
+#
+# and attempts to check it out when the work tree has:
+#
+#     path2/file0 - a file in a directory
+#     path3       - a symlink pointing at "path2"
+#
+# Checkout cache should fail to extract path3/file1 because the leading
+# path path3 is occupied by a non-directory.  With "-f" it should remove
+# the symlink path3 and create directory path3 and file path3/file1.
+
+mkdir path2
+date >path2/file0
+test_expect_success \
+    'git update-index --add path2/file0' \
+    'git update-index --add path2/file0'
+test_expect_success \
+    'writing tree out with git write-tree' \
+    'tree1=$(git write-tree)'
+test_debug 'show_files $tree1'
+
+mkdir path3
+date >path3/file1
+test_expect_success \
+    'git update-index --add path3/file1' \
+    'git update-index --add path3/file1'
+test_expect_success \
+    'writing tree out with git write-tree' \
+    'tree2=$(git write-tree)'
+test_debug 'show_files $tree2'
+
+rm -fr path3
+test_expect_success \
+    'read previously written tree and checkout.' \
+    'git read-tree -m $tree1 && git checkout-index -f -a'
+test_debug 'show_files $tree1'
+
+test_expect_success \
+    'add a symlink' \
+    'test_ln_s_add path2 path3'
+test_expect_success \
+    'writing tree out with git write-tree' \
+    'tree3=$(git write-tree)'
+test_debug 'show_files $tree3'
+
+# Morten says "Got that?" here.
+# Test begins.
+
+test_expect_success \
+    'read previously written tree and checkout.' \
+    'git read-tree $tree2 && git checkout-index -f -a'
+test_debug 'show_files $tree2'
+
+test_expect_success \
+    'checking out conflicting path with -f' \
+    'test ! -h path2 && test -d path2 &&
+     test ! -h path3 && test -d path3 &&
+     test ! -h path2/file0 && test -f path2/file0 &&
+     test ! -h path3/file1 && test -f path3/file1'
+
+test_done
diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh
deleted file mode 100755 (executable)
index 1fc8e63..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-
-test_description='git checkout-index test.
-
-This test registers the following filesystem structure in the cache:
-
-    path0/file0        - a file in a directory
-    path1/file1 - a file in a directory
-
-and attempts to check it out when the work tree has:
-
-    path0/file0 - a file in a directory
-    path1       - a symlink pointing at "path0"
-
-Checkout cache should fail to extract path1/file1 because the leading
-path path1 is occupied by a non-directory.  With "-f" it should remove
-the symlink path1 and create directory path1 and file path1/file1.
-'
-. ./test-lib.sh
-
-show_files() {
-       # show filesystem files, just [-dl] for type and name
-       find path? -ls |
-       sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /'
-       # what's in the cache, just mode and name
-       git ls-files --stage |
-       sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /'
-       # what's in the tree, just mode and name.
-       git ls-tree -r "$1" |
-       sed -e 's/^\([0-9]*\)   [^ ]*   [0-9a-f]*       /tr: \1 /'
-}
-
-mkdir path0
-date >path0/file0
-test_expect_success \
-    'git update-index --add path0/file0' \
-    'git update-index --add path0/file0'
-test_expect_success \
-    'writing tree out with git write-tree' \
-    'tree1=$(git write-tree)'
-test_debug 'show_files $tree1'
-
-mkdir path1
-date >path1/file1
-test_expect_success \
-    'git update-index --add path1/file1' \
-    'git update-index --add path1/file1'
-test_expect_success \
-    'writing tree out with git write-tree' \
-    'tree2=$(git write-tree)'
-test_debug 'show_files $tree2'
-
-rm -fr path1
-test_expect_success \
-    'read previously written tree and checkout.' \
-    'git read-tree -m $tree1 && git checkout-index -f -a'
-test_debug 'show_files $tree1'
-
-test_expect_success \
-    'add a symlink' \
-    'test_ln_s_add path0 path1'
-test_expect_success \
-    'writing tree out with git write-tree' \
-    'tree3=$(git write-tree)'
-test_debug 'show_files $tree3'
-
-# Morten says "Got that?" here.
-# Test begins.
-
-test_expect_success \
-    'read previously written tree and checkout.' \
-    'git read-tree $tree2 && git checkout-index -f -a'
-test_debug 'show_files $tree2'
-
-test_expect_success \
-    'checking out conflicting path with -f' \
-    'test ! -h path0 && test -d path0 &&
-     test ! -h path1 && test -d path1 &&
-     test ! -h path0/file0 && test -f path0/file0 &&
-     test ! -h path1/file1 && test -f path1/file1'
-
-test_done
index 045aca1c18bec3d5278eb50a461df0588dd84618..6aae36417122a8af5d010910fb2586aa9ec91815 100755 (executable)
@@ -122,6 +122,35 @@ test_expect_success 'changed commit' '
        test_cmp expected actual
 '
 
+test_expect_success 'changed commit with sm config' '
+       git range-diff --no-color --submodule=log topic...changed >actual &&
+       cat >expected <<-EOF &&
+       1:  4de457d = 1:  a4b3333 s/5/A/
+       2:  fccce22 = 2:  f51d370 s/4/A/
+       3:  147e64e ! 3:  0559556 s/11/B/
+           @@ -10,7 +10,7 @@
+             9
+             10
+            -11
+           -+B
+           ++BB
+             12
+             13
+             14
+       4:  a63e992 ! 4:  d966c5c s/12/B/
+           @@ -8,7 +8,7 @@
+            @@
+             9
+             10
+           - B
+           + BB
+            -12
+            +B
+             13
+       EOF
+       test_cmp expected actual
+'
+
 test_expect_success 'no commits on one side' '
        git commit --amend -m "new message" &&
        git range-diff master HEAD@{1} HEAD
index ff89b6341a6fc19801959058e35039778bc6577b..d60e59ecdb87d1a9722340acd09af82e29cb9dca 100755 (executable)
@@ -75,6 +75,16 @@ test_expect_success 'rebase --keep-empty' '
        test_line_count = 6 actual
 '
 
+cat > expect <<EOF
+error: nothing to do
+EOF
+
+test_expect_success 'rebase -i with empty HEAD' '
+       set_fake_editor &&
+       test_must_fail env FAKE_LINES="1 exec_true" git rebase -i HEAD^ >actual 2>&1 &&
+       test_i18ncmp expect actual
+'
+
 test_expect_success 'rebase -i with the exec command' '
        git checkout master &&
        (
index 25099d715cee0dff321c75b944f25f58169bf645..4c3f7d8dfea1fb087c839c3a039e728642869975 100755 (executable)
@@ -241,5 +241,16 @@ test_rerere_autoupdate -m
 GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
 test_rerere_autoupdate -i
 test_rerere_autoupdate --preserve-merges
+unset GIT_SEQUENCE_EDITOR
+
+test_expect_success 'the todo command "break" works' '
+       rm -f execed &&
+       FAKE_LINES="break b exec_>execed" git rebase -i HEAD &&
+       test_path_is_missing execed &&
+       git rebase --continue &&
+       test_path_is_missing execed &&
+       git rebase --continue &&
+       test_path_is_file execed
+'
 
 test_done
index 0c4eefec760efaa238c1718ac7353e14a6e95344..f355c6825a4a5ff6adef656c432a4e2fdaf0fea9 100755 (executable)
@@ -351,4 +351,14 @@ test_expect_success 'autostash is saved on editor failure with conflict' '
        test_cmp expected file0
 '
 
+test_expect_success 'autostash with dirty submodules' '
+       test_when_finished "git reset --hard && git checkout master" &&
+       git checkout -b with-submodule &&
+       git submodule add ./ sub &&
+       test_tick &&
+       git commit -m add-submodule &&
+       echo changed >sub/file0 &&
+       git rebase -i --autostash HEAD
+'
+
 test_done
index 453e6c35eb89fd82649401cb20a462c560a102cf..6e0dd6f9e5c482631147bb4770498dc4e7a8165f 100755 (executable)
@@ -127,4 +127,14 @@ test_expect_success 'diff --no-index from repo subdir respects config (implicit)
        test_cmp expect actual.head
 '
 
+test_expect_success 'diff --no-index from repo subdir with absolute paths' '
+       cat <<-EOF >expect &&
+       1       1       $(pwd)/non/git/{a => b}
+       EOF
+       test_expect_code 1 \
+               git -C repo/sub diff --numstat \
+               "$(pwd)/non/git/a" "$(pwd)/non/git/b" >actual &&
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4214-log-graph-octopus.sh b/t/t4214-log-graph-octopus.sh
new file mode 100755 (executable)
index 0000000..dab96c8
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='git log --graph of skewed left octopus merge.'
+
+. ./test-lib.sh
+
+test_expect_success 'set up merge history' '
+       cat >expect.uncolored <<-\EOF &&
+       * left
+       | *---.   octopus-merge
+       | |\ \ \
+       |/ / / /
+       | | | * 4
+       | | * | 3
+       | | |/
+       | * | 2
+       | |/
+       * | 1
+       |/
+       * initial
+       EOF
+       cat >expect.colors <<-\EOF &&
+       * left
+       <RED>|<RESET> *<BLUE>-<RESET><BLUE>-<RESET><MAGENTA>-<RESET><MAGENTA>.<RESET>   octopus-merge
+       <RED>|<RESET> <RED>|<RESET><YELLOW>\<RESET> <BLUE>\<RESET> <MAGENTA>\<RESET>
+       <RED>|<RESET><RED>/<RESET> <YELLOW>/<RESET> <BLUE>/<RESET> <MAGENTA>/<RESET>
+       <RED>|<RESET> <YELLOW>|<RESET> <BLUE>|<RESET> * 4
+       <RED>|<RESET> <YELLOW>|<RESET> * <MAGENTA>|<RESET> 3
+       <RED>|<RESET> <YELLOW>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
+       <RED>|<RESET> * <MAGENTA>|<RESET> 2
+       <RED>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
+       * <MAGENTA>|<RESET> 1
+       <MAGENTA>|<RESET><MAGENTA>/<RESET>
+       * initial
+       EOF
+       test_commit initial &&
+       for i in 1 2 3 4 ; do
+               git checkout master -b $i || return $?
+               # Make tag name different from branch name, to avoid
+               # ambiguity error when calling checkout.
+               test_commit $i $i $i tag$i || return $?
+       done &&
+       git checkout 1 -b merge &&
+       test_tick &&
+       git merge -m octopus-merge 1 2 3 4 &&
+       git checkout 1 -b L &&
+       test_commit left
+'
+
+test_expect_success 'log --graph with tricky octopus merge with colors' '
+       test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
+       git log --color=always --graph --date-order --pretty=tformat:%s --all >actual.colors.raw &&
+       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
+       test_cmp expect.colors actual.colors
+'
+
+test_expect_success 'log --graph with tricky octopus merge, no color' '
+       git log --color=never --graph --date-order --pretty=tformat:%s --all >actual.raw &&
+       sed "s/ *\$//" actual.raw >actual &&
+       test_cmp expect.uncolored actual
+'
+
+# Repeat the previous two tests with "normal" octopus merge (i.e.,
+# without the first parent skewing to the "left" branch column).
+
+test_expect_success 'log --graph with normal octopus merge, no color' '
+       cat >expect.uncolored <<-\EOF &&
+       *---.   octopus-merge
+       |\ \ \
+       | | | * 4
+       | | * | 3
+       | | |/
+       | * | 2
+       | |/
+       * | 1
+       |/
+       * initial
+       EOF
+       git log --color=never --graph --date-order --pretty=tformat:%s merge >actual.raw &&
+       sed "s/ *\$//" actual.raw >actual &&
+       test_cmp expect.uncolored actual
+'
+
+test_expect_success 'log --graph with normal octopus merge with colors' '
+       cat >expect.colors <<-\EOF &&
+       *<YELLOW>-<RESET><YELLOW>-<RESET><BLUE>-<RESET><BLUE>.<RESET>   octopus-merge
+       <RED>|<RESET><GREEN>\<RESET> <YELLOW>\<RESET> <BLUE>\<RESET>
+       <RED>|<RESET> <GREEN>|<RESET> <YELLOW>|<RESET> * 4
+       <RED>|<RESET> <GREEN>|<RESET> * <BLUE>|<RESET> 3
+       <RED>|<RESET> <GREEN>|<RESET> <BLUE>|<RESET><BLUE>/<RESET>
+       <RED>|<RESET> * <BLUE>|<RESET> 2
+       <RED>|<RESET> <BLUE>|<RESET><BLUE>/<RESET>
+       * <BLUE>|<RESET> 1
+       <BLUE>|<RESET><BLUE>/<RESET>
+       * initial
+       EOF
+       test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
+       git log --color=always --graph --date-order --pretty=tformat:%s merge >actual.colors.raw &&
+       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
+       test_cmp expect.colors actual.colors
+'
+test_done
index 2a97b27b0a68f94ab7204764ced5fd1457da3d9c..602bfd9574350ae7f851d6aa26d978cc919f5c58 100755 (executable)
@@ -206,6 +206,12 @@ test_expect_success 'git archive with --output, override inferred format' '
        test_cmp_bin b.tar d4.zip
 '
 
+test_expect_success GZIP 'git archive with --output and --remote creates .tgz' '
+       git archive --output=d5.tgz --remote=. HEAD &&
+       gzip -d -c <d5.tgz >d5.tar &&
+       test_cmp_bin b.tar d5.tar
+'
+
 test_expect_success 'git archive --list outside of a git repo' '
        nongit git archive --list
 '
index 55c78709978ff75cdbd794ee4c816dfd1eb55ef0..106eddbd85b04ac8539b722ade63936e28325840 100755 (executable)
@@ -158,11 +158,16 @@ test_expect_success 'git archive --format=zip with --output' \
     'git archive --format=zip --output=d2.zip HEAD &&
     test_cmp_bin d.zip d2.zip'
 
-test_expect_success 'git archive with --output, inferring format' '
+test_expect_success 'git archive with --output, inferring format (local)' '
        git archive --output=d3.zip HEAD &&
        test_cmp_bin d.zip d3.zip
 '
 
+test_expect_success 'git archive with --output, inferring format (remote)' '
+       git archive --remote=. --output=d4.zip HEAD &&
+       test_cmp_bin d.zip d4.zip
+'
+
 test_expect_success \
     'git archive --format=zip with prefix' \
     'git archive --format=zip --prefix=prefix/ HEAD >e.zip'
index 2e718f0bdede397d399399536707743f9f7cff34..24541ea137517354a8b738bdef34728b49f2850e 100755 (executable)
@@ -67,6 +67,47 @@ test_expect_success 'verify normal and blob:none packfiles have same commits/tre
        test_cmp expected observed
 '
 
+test_expect_success 'get an error for missing tree object' '
+       git init r5 &&
+       echo foo >r5/foo &&
+       git -C r5 add foo &&
+       git -C r5 commit -m "foo" &&
+       del=$(git -C r5 rev-parse HEAD^{tree} | sed "s|..|&/|") &&
+       rm r5/.git/objects/$del &&
+       test_must_fail git -C r5 pack-objects --rev --stdout 2>bad_tree <<-EOF &&
+       HEAD
+       EOF
+       grep "bad tree object" bad_tree
+'
+
+test_expect_success 'setup for tests of tree:0' '
+       mkdir r1/subtree &&
+       echo "This is a file in a subtree" >r1/subtree/file &&
+       git -C r1 add subtree/file &&
+       git -C r1 commit -m subtree
+'
+
+test_expect_success 'verify tree:0 packfile has no blobs or trees' '
+       git -C r1 pack-objects --rev --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
+       HEAD
+       EOF
+       git -C r1 index-pack ../commitsonly.pack &&
+       git -C r1 verify-pack -v ../commitsonly.pack >objs &&
+       ! grep -E "tree|blob" objs
+'
+
+test_expect_success 'grab tree directly when using tree:0' '
+       # We should get the tree specified directly but not its blobs or subtrees.
+       git -C r1 pack-objects --rev --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
+       HEAD:
+       EOF
+       git -C r1 index-pack ../commitsonly.pack &&
+       git -C r1 verify-pack -v ../commitsonly.pack >objs &&
+       awk "/tree|blob/{print \$1}" objs >trees_and_blobs &&
+       git -C r1 rev-parse HEAD: >expected &&
+       test_cmp expected trees_and_blobs
+'
+
 # Test blob:limit=<n>[kmg] filter.
 # We boundary test around the size parameter.  The filter is strictly less than
 # the value, so size 500 and 1000 should have the same results, but 1001 should
diff --git a/t/t5321-pack-large-objects.sh b/t/t5321-pack-large-objects.sh
new file mode 100755 (executable)
index 0000000..a75eab8
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Copyright (c) 2018 Johannes Schindelin
+#
+
+test_description='git pack-object with "large" deltas
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pack.sh
+
+# Two similar-ish objects that we have computed deltas between.
+A=01d7713666f4de822776c7622c10f1b07de280dc
+B=e68fe8129b546b101aee9510c5328e7f21ca1d18
+
+test_expect_success 'setup' '
+       clear_packs &&
+       {
+               pack_header 2 &&
+               pack_obj $A $B &&
+               pack_obj $B
+       } >ab.pack &&
+       pack_trailer ab.pack &&
+       git index-pack --stdin <ab.pack
+'
+
+test_expect_success 'repack large deltas' '
+       printf "%s\\n" $A $B |
+       GIT_TEST_OE_DELTA_SIZE=2 git pack-objects tmp-pack
+'
+
+test_done
diff --git a/t/t5410-receive-pack-alternates.sh b/t/t5410-receive-pack-alternates.sh
new file mode 100755 (executable)
index 0000000..f00d0da
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='git receive-pack with alternate ref filtering'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit base &&
+       git clone -s --bare . fork &&
+       git checkout -b public/branch master &&
+       test_commit public &&
+       git checkout -b private/branch master &&
+       test_commit private
+'
+
+extract_haves () {
+       depacketize | perl -lne '/^(\S+) \.have/ and print $1'
+}
+
+test_expect_success 'with core.alternateRefsCommand' '
+       write_script fork/alternate-refs <<-\EOF &&
+               git --git-dir="$1" for-each-ref \
+                       --format="%(objectname)" \
+                       refs/heads/public/
+       EOF
+       test_config -C fork core.alternateRefsCommand ./alternate-refs &&
+       git rev-parse public/branch >expect &&
+       printf "0000" | git receive-pack fork >actual &&
+       extract_haves <actual >actual.haves &&
+       test_cmp expect actual.haves
+'
+
+test_expect_success 'with core.alternateRefsPrefixes' '
+       test_config -C fork core.alternateRefsPrefixes "refs/heads/private" &&
+       git rev-parse private/branch >expect &&
+       printf "0000" | git receive-pack fork >actual &&
+       extract_haves <actual >actual.haves &&
+       test_cmp expect actual.haves
+'
+
+test_done
index 7a8f56db53eb6c3b869d525501c186bd2df1ba0a..7316365a24679edbb9d79db74a4ba6e9aa4ad9be 100755 (executable)
@@ -1577,7 +1577,13 @@ test_expect_success 'receive.denyCurrentBranch = updateInstead' '
                test $(git -C .. rev-parse master) = $(git rev-parse HEAD) &&
                git diff --quiet &&
                git diff --cached --quiet
-       )
+       ) &&
+
+       # (6) updateInstead intervened by fast-forward check
+       test_must_fail git push void master^:master &&
+       test $(git -C void rev-parse HEAD) = $(git rev-parse master) &&
+       git -C void diff --quiet &&
+       git -C void diff --cached --quiet
 '
 
 test_expect_success 'updateInstead with push-to-checkout hook' '
index 7045685e2d3942161084ecd04a116faa83f35a44..6faf17e17a133f31245f89fd0fe9bad1687ac5fa 100755 (executable)
@@ -186,6 +186,33 @@ EOF
        test_cmp expect actual
 '
 
+test_expect_success '.git/shallow is edited by repack' '
+       git init shallow-server &&
+       test_commit -C shallow-server A &&
+       test_commit -C shallow-server B &&
+       git -C shallow-server checkout -b branch &&
+       test_commit -C shallow-server C &&
+       test_commit -C shallow-server E &&
+       test_commit -C shallow-server D &&
+       d="$(git -C shallow-server rev-parse --verify D^0)" &&
+       git -C shallow-server checkout master &&
+
+       git clone --depth=1 --no-tags --no-single-branch \
+               "file://$PWD/shallow-server" shallow-client &&
+
+       : now remove the branch and fetch with prune &&
+       git -C shallow-server branch -D branch &&
+       git -C shallow-client fetch --prune --depth=1 \
+               origin "+refs/heads/*:refs/remotes/origin/*" &&
+       git -C shallow-client repack -adfl &&
+       test_must_fail git -C shallow-client rev-parse --verify $d^0 &&
+       ! grep $d shallow-client/.git/shallow &&
+
+       git -C shallow-server branch branch-orig $d &&
+       git -C shallow-client fetch --prune --depth=2 \
+               origin "+refs/heads/*:refs/remotes/origin/*"
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
index 3dc8f8ecec2c03c63788bc5b0e9db88e55deb823..8630b0cc39045f913987463a36388bcd72d6e248 100755 (executable)
@@ -381,6 +381,21 @@ test_expect_success 'using fetch command in remote-curl updates refs' '
        test_cmp expect actual
 '
 
+test_expect_success 'fetch by SHA-1 without tag following' '
+       SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
+       rm -rf "$SERVER" client &&
+
+       git init "$SERVER" &&
+       test_commit -C "$SERVER" foo &&
+
+       git clone $HTTPD_URL/smart/server client &&
+
+       test_commit -C "$SERVER" bar &&
+       git -C "$SERVER" rev-parse bar >bar_hash &&
+       git -C client -c protocol.version=0 fetch \
+               --no-tags origin $(cat bar_hash)
+'
+
 test_expect_success 'GIT_REDACT_COOKIES redacts cookies' '
        rm -rf clone &&
        echo "Set-Cookie: Foo=1" >cookies &&
index 6ff6146923d945d9514ad5871b845969c2efb269..336f02a41a66b48269e8eb6030d5323cffb8b686 100755 (executable)
@@ -166,6 +166,48 @@ test_expect_success 'partial clone with transfer.fsckobjects=1 uses index-pack -
        grep "git index-pack.*--fsck-objects" trace
 '
 
+test_expect_success 'use fsck before and after manually fetching a missing subtree' '
+       # push new commit so server has a subtree
+       mkdir src/dir &&
+       echo "in dir" >src/dir/file.txt &&
+       git -C src add dir/file.txt &&
+       git -C src commit -m "file in dir" &&
+       git -C src push -u srv master &&
+       SUBTREE=$(git -C src rev-parse HEAD:dir) &&
+
+       rm -rf dst &&
+       git clone --no-checkout --filter=tree:0 "file://$(pwd)/srv.bare" dst &&
+       git -C dst fsck &&
+
+       # Make sure we only have commits, and all trees and blobs are missing.
+       git -C dst rev-list --missing=allow-any --objects master \
+               >fetched_objects &&
+       awk -f print_1.awk fetched_objects |
+       xargs -n1 git -C dst cat-file -t >fetched_types &&
+
+       sort -u fetched_types >unique_types.observed &&
+       echo commit >unique_types.expected &&
+       test_cmp unique_types.expected unique_types.observed &&
+
+       # Auto-fetch a tree with cat-file.
+       git -C dst cat-file -p $SUBTREE >tree_contents &&
+       grep file.txt tree_contents &&
+
+       # fsck still works after an auto-fetch of a tree.
+       git -C dst fsck &&
+
+       # Auto-fetch all remaining trees and blobs with --missing=error
+       git -C dst rev-list --missing=error --objects master >fetched_objects &&
+       test_line_count = 70 fetched_objects &&
+
+       awk -f print_1.awk fetched_objects |
+       xargs -n1 git -C dst cat-file -t >fetched_types &&
+
+       sort -u fetched_types >unique_types.observed &&
+       test_write_lines blob commit tree >unique_types.expected &&
+       test_cmp unique_types.expected unique_types.observed
+'
+
 test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
        rm -rf src dst &&
        git init src &&
@@ -182,6 +224,23 @@ test_expect_success 'partial clone fetches blobs pointed to by refs even if norm
        git -C dst fsck
 '
 
+test_expect_success 'fetch what is specified on CLI even if already promised' '
+       rm -rf src dst.git &&
+       git init src &&
+       test_commit -C src foo &&
+       test_config -C src uploadpack.allowfilter 1 &&
+       test_config -C src uploadpack.allowanysha1inwant 1 &&
+
+       git hash-object --stdin <src/foo.t >blob &&
+
+       git clone --bare --filter=blob:none "file://$(pwd)/src" dst.git &&
+       git -C dst.git rev-list --objects --quiet --missing=print HEAD >missing_before &&
+       grep "?$(cat blob)" missing_before &&
+       git -C dst.git fetch origin $(cat blob) &&
+       git -C dst.git rev-list --objects --quiet --missing=print HEAD >missing_after &&
+       ! grep "?$(cat blob)" missing_after
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
index 8e3b773e3f5b96b8429ec75ba7d5e233276f34ba..6ab8dea8cd896f5c7656755c89cee9759804bef9 100755 (executable)
@@ -79,6 +79,19 @@ test_expect_success 'fetch with git:// using protocol v2' '
        grep "fetch< version 2" log
 '
 
+test_expect_success 'fetch by hash without tag following with protocol v2 does not list refs' '
+       test_when_finished "rm -f log" &&
+
+       test_commit -C "$daemon_parent" two_a &&
+       git -C "$daemon_parent" rev-parse two_a >two_a_hash &&
+
+       GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \
+               fetch --no-tags origin $(cat two_a_hash) &&
+
+       grep "fetch< version 2" log &&
+       ! grep "fetch> command=ls-refs" log
+'
+
 test_expect_success 'pull with git:// using protocol v2' '
        test_when_finished "rm -f log" &&
 
@@ -286,6 +299,10 @@ test_expect_success 'dynamically fetch missing object' '
        grep "version 2" trace
 '
 
+test_expect_success 'when dynamically fetching missing object, do not list refs' '
+       ! grep "git> command=ls-refs" trace
+'
+
 test_expect_success 'partial fetch' '
        rm -rf client "$(pwd)/trace" &&
        git init client &&
index 59e52c5a09eebfabe932c5c33a842236f68ffdfb..e1cef58f2af96f6185ed93086c3966fb73d58f79 100755 (executable)
@@ -230,13 +230,13 @@ test_expect_success 'git detects differently handled merges conflict' '
                        :2:new_a :3:new_a &&
                test_cmp expect actual &&
 
-               git cat-file -p B:new_a >ours &&
-               git cat-file -p C:new_a >theirs &&
+               git cat-file -p C:new_a >ours &&
+               git cat-file -p B:new_a >theirs &&
                >empty &&
                test_must_fail git merge-file \
-                       -L "Temporary merge branch 2" \
-                       -L "" \
                        -L "Temporary merge branch 1" \
+                       -L "" \
+                       -L "Temporary merge branch 2" \
                        ours empty theirs &&
                sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
                git cat-file -p :1:new_a >actual &&
index 53975c572465314957aaef360dea040a5be2db22..eb32505a6ef8804f4f7762ce0332f00beeacbe99 100755 (executable)
@@ -34,6 +34,18 @@ test_expect_success 'verify blob:none omits all 5 blobs' '
        test_cmp expected observed
 '
 
+test_expect_success 'specify blob explicitly prevents filtering' '
+       file_3=$(git -C r1 ls-files -s file.3 |
+                awk -f print_2.awk) &&
+
+       file_4=$(git -C r1 ls-files -s file.4 |
+                awk -f print_2.awk) &&
+
+       git -C r1 rev-list --objects --filter=blob:none HEAD $file_3 >observed &&
+       grep "$file_3" observed &&
+       ! grep "$file_4" observed
+'
+
 test_expect_success 'verify emitted+omitted == all' '
        git -C r1 rev-list --objects HEAD >revs &&
        awk -f print_1.awk revs |
@@ -232,6 +244,56 @@ test_expect_success 'verify sparse:oid=oid-ish omits top-level files' '
        test_cmp expected observed
 '
 
+test_expect_success 'rev-list W/ --missing=print and --missing=allow-any for trees' '
+       TREE=$(git -C r3 rev-parse HEAD:dir1) &&
+
+       # Create a spare repo because we will be deleting objects from this one.
+       git clone r3 r3.b &&
+
+       rm r3.b/.git/objects/$(echo $TREE | sed "s|^..|&/|") &&
+
+       git -C r3.b rev-list --quiet --missing=print --objects HEAD \
+               >missing_objs 2>rev_list_err &&
+       echo "?$TREE" >expected &&
+       test_cmp expected missing_objs &&
+
+       # do not complain when a missing tree cannot be parsed
+       test_must_be_empty rev_list_err &&
+
+       git -C r3.b rev-list --missing=allow-any --objects HEAD \
+               >objs 2>rev_list_err &&
+       ! grep $TREE objs &&
+       test_must_be_empty rev_list_err
+'
+
+# Test tree:0 filter.
+
+test_expect_success 'verify tree:0 includes trees in "filtered" output' '
+       git -C r3 rev-list --quiet --objects --filter-print-omitted \
+               --filter=tree:0 HEAD >revs &&
+
+       awk -f print_1.awk revs |
+       sed s/~// |
+       xargs -n1 git -C r3 cat-file -t >unsorted_filtered_types &&
+
+       sort -u unsorted_filtered_types >filtered_types &&
+       test_write_lines blob tree >expected &&
+       test_cmp expected filtered_types
+'
+
+# Make sure tree:0 does not iterate through any trees.
+
+test_expect_success 'filter a GIANT tree through tree:0' '
+       GIT_TRACE=1 git -C r3 rev-list \
+               --objects --filter=tree:0 HEAD 2>filter_trace &&
+       grep "Skipping contents of tree [.][.][.]" filter_trace >actual &&
+       # One line for each commit traversed.
+       test_line_count = 2 actual &&
+
+       # Make sure no other trees were considered besides the root.
+       ! grep "Skipping contents of tree [^.]" filter_trace
+'
+
 # Delete some loose objects and use rev-list, but WITHOUT any filtering.
 # This models previously omitted objects that we did not receive.
 
index b2ca77b3384c97954991eae9c5af89c4d8d8035e..5fcf281dfbf8d601fc934d890db79f7284b19402 100755 (executable)
@@ -112,7 +112,7 @@ do
 done
 
 test_expect_success 'editor with a space' '
-       echo "echo space >\$1" >"e space.sh" &&
+       echo "echo space >\"\$1\"" >"e space.sh" &&
        chmod a+x "e space.sh" &&
        GIT_EDITOR="./e\ space.sh" git commit --amend &&
        test space = "$(git show -s --pretty=format:%s)"
index c0ffc1022aedf1e7248388bdca06d071108c3b61..76a7cb0af7955aa618556a43cc9f2a568d8de3e8 100755 (executable)
@@ -1224,6 +1224,30 @@ test_expect_success 'submodule update and setting submodule.<name>.active' '
        test_cmp expect actual
 '
 
+test_expect_success 'clone active submodule without submodule url set' '
+       test_when_finished "rm -rf test/test" &&
+       mkdir test &&
+       # another dir breaks accidental relative paths still being correct
+       git clone file://"$pwd"/multisuper test/test &&
+       (
+               cd test/test &&
+               git config submodule.active "." &&
+
+               # do not pass --init flag, as the submodule is already active:
+               git submodule update &&
+               git submodule status >actual_raw &&
+
+               cut -c 1,43- actual_raw >actual &&
+               cat >expect <<-\EOF &&
+                sub0 (test2)
+                sub1 (test2)
+                sub2 (test2)
+                sub3 (test2)
+               EOF
+               test_cmp expect actual
+       )
+'
+
 test_expect_success 'clone --recurse-submodules with a pathspec works' '
        test_when_finished "rm -rf multisuper_clone" &&
        cat >expected <<-\EOF &&
similarity index 99%
rename from t/t7500-commit.sh
rename to t/t7500-commit-template-squash-signoff.sh
index 31ab608b6700829427b5aeafa65222469e494d26..46a5cd4b7395ce8bba29763644a6fbd6739c929e 100755 (executable)
@@ -5,7 +5,7 @@
 
 test_description='git commit
 
-Tests for selected commit options.'
+Tests for template, signoff, squash and -F functions.'
 
 . ./test-lib.sh
 
similarity index 98%
rename from t/t7501-commit.sh
rename to t/t7501-commit-basic-functionality.sh
index 1a6773ee6889939a0046664bd8a7cdcc4f21fe01..f1349af56edd17483120a1e177c70282759778d3 100755 (executable)
@@ -99,12 +99,12 @@ test_expect_success '--dry-run with stuff to commit returns ok' '
        git commit -m next -a --dry-run
 '
 
-test_expect_failure '--short with stuff to commit returns ok' '
+test_expect_success '--short with stuff to commit returns ok' '
        echo bongo bongo bongo >>file &&
        git commit -m next -a --short
 '
 
-test_expect_failure '--porcelain with stuff to commit returns ok' '
+test_expect_success '--porcelain with stuff to commit returns ok' '
        echo bongo bongo bongo >>file &&
        git commit -m next -a --porcelain
 '
@@ -698,4 +698,10 @@ test_expect_success '--dry-run with conflicts fixed from a merge' '
        git commit -m "conflicts fixed from merge."
 '
 
+test_expect_success '--dry-run --short' '
+       >test-file &&
+       git add test-file &&
+       git commit --dry-run --short
+'
+
 test_done
similarity index 98%
rename from t/t7509-commit.sh
rename to t/t7509-commit-authorship.sh
index ddef7ea6b0d87ef5922bf267ee3fdc9ade4383af..500ab2fe72288ea03b28c547130079a663984316 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2009 Erick Mattos
 #
 
-test_description='git commit --reset-author'
+test_description='commit tests of various authorhip options. '
 
 . ./test-lib.sh
 
index 4e37ff8f169615a893ee5d0594ff056b0284b724..19ccae28699da7949af3203cde62ac648c7effb4 100755 (executable)
@@ -175,8 +175,9 @@ test_expect_success GPG 'show good signature with custom format' '
        G
        13B6F51ECDDE430D
        C O Mitter <committer@example.com>
+       73D758744BE721698EC54E8713B6F51ECDDE430D
        EOF
-       git log -1 --format="%G?%n%GK%n%GS" sixth-signed >actual &&
+       git log -1 --format="%G?%n%GK%n%GS%n%GF" sixth-signed >actual &&
        test_cmp expect actual
 '
 
@@ -185,8 +186,9 @@ test_expect_success GPG 'show bad signature with custom format' '
        B
        13B6F51ECDDE430D
        C O Mitter <committer@example.com>
+
        EOF
-       git log -1 --format="%G?%n%GK%n%GS" $(cat forged1.commit) >actual &&
+       git log -1 --format="%G?%n%GK%n%GS%n%GF" $(cat forged1.commit) >actual &&
        test_cmp expect actual
 '
 
@@ -195,8 +197,9 @@ test_expect_success GPG 'show untrusted signature with custom format' '
        U
        61092E85B7227189
        Eris Discordia <discord@example.net>
+       D4BE22311AD3131E5EDA29A461092E85B7227189
        EOF
-       git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual &&
+       git log -1 --format="%G?%n%GK%n%GS%n%GF" eighth-signed-alt >actual &&
        test_cmp expect actual
 '
 
@@ -205,8 +208,9 @@ test_expect_success GPG 'show unknown signature with custom format' '
        E
        61092E85B7227189
 
+
        EOF
-       GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual &&
+       GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GK%n%GS%n%GF" eighth-signed-alt >actual &&
        test_cmp expect actual
 '
 
@@ -215,8 +219,9 @@ test_expect_success GPG 'show lack of signature with custom format' '
        N
 
 
+
        EOF
-       git log -1 --format="%G?%n%GK%n%GS" seventh-unsigned >actual &&
+       git log -1 --format="%G?%n%GK%n%GS%n%GF" seventh-unsigned >actual &&
        test_cmp expect actual
 '
 
@@ -234,4 +239,31 @@ test_expect_success GPG 'check config gpg.format values' '
        test_must_fail git commit -S --amend -m "fail"
 '
 
+test_expect_success GPG 'detect fudged commit with double signature' '
+       sed -e "/gpgsig/,/END PGP/d" forged1 >double-base &&
+       sed -n -e "/gpgsig/,/END PGP/p" forged1 | \
+               sed -e "s/^gpgsig//;s/^ //" | gpg --dearmor >double-sig1.sig &&
+       gpg -o double-sig2.sig -u 29472784 --detach-sign double-base &&
+       cat double-sig1.sig double-sig2.sig | gpg --enarmor >double-combined.asc &&
+       sed -e "s/^\(-.*\)ARMORED FILE/\1SIGNATURE/;1s/^/gpgsig /;2,\$s/^/ /" \
+               double-combined.asc > double-gpgsig &&
+       sed -e "/committer/r double-gpgsig" double-base >double-commit &&
+       git hash-object -w -t commit double-commit >double-commit.commit &&
+       test_must_fail git verify-commit $(cat double-commit.commit) &&
+       git show --pretty=short --show-signature $(cat double-commit.commit) >double-actual &&
+       grep "BAD signature from" double-actual &&
+       grep "Good signature from" double-actual
+'
+
+test_expect_success GPG 'show double signature with custom format' '
+       cat >expect <<-\EOF &&
+       E
+
+
+
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS%n%GF" $(cat double-commit.commit) >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 8384ad258c359572d83bb38fd69f61ec6fe612e7..3e0a61db2348577ed5c6cef4f844c2a26548de1d 100755 (executable)
@@ -4,13 +4,6 @@ test_description='git status with file system watcher'
 
 . ./test-lib.sh
 
-#
-# To run the entire git test suite using fsmonitor:
-#
-# copy t/t7519/fsmonitor-all to a location in your path and then set
-# GIT_FSMONITOR_TEST=fsmonitor-all and run your tests.
-#
-
 # Note, after "git reset --hard HEAD" no extensions exist other than 'TREE'
 # "git update-index --fsmonitor" can be used to get the extension written
 # before testing the results.
@@ -245,9 +238,9 @@ do
                git config core.preloadIndex $preload_val &&
                if test $preload_val = true
                then
-                       GIT_FORCE_PRELOAD_TEST=$preload_val; export GIT_FORCE_PRELOAD_TEST
+                       GIT_TEST_PRELOAD_INDEX=$preload_val; export GIT_TEST_PRELOAD_INDEX
                else
-                       unset GIT_FORCE_PRELOAD_TEST
+                       sane_unset GIT_TEST_PRELOAD_INDEX
                fi
        '
 
index be5c1bd553ede55d54e1d918625c99000ba32d80..43aa4161cf030b4c58ab2785e0bffc6635127a5f 100755 (executable)
@@ -309,6 +309,8 @@ do
                        echo ${HC}v:1:vvv
                } >expected &&
                git grep --max-depth -1 -n -e vvv $H >actual &&
+               test_cmp expected actual &&
+               git grep --recursive -n -e vvv $H >actual &&
                test_cmp expected actual
        '
 
@@ -317,6 +319,8 @@ do
                        echo ${HC}v:1:vvv
                } >expected &&
                git grep --max-depth 0 -n -e vvv $H >actual &&
+               test_cmp expected actual &&
+               git grep --no-recursive -n -e vvv $H >actual &&
                test_cmp expected actual
        '
 
@@ -327,6 +331,8 @@ do
                        echo ${HC}v:1:vvv
                } >expected &&
                git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
+               test_cmp expected actual &&
+               git grep --no-recursive -n -e vvv $H -- "*" >actual &&
                test_cmp expected actual
        '
 
@@ -344,6 +350,8 @@ do
                        echo ${HC}t/v:1:vvv
                } >expected &&
                git grep --max-depth 0 -n -e vvv $H -- t >actual &&
+               test_cmp expected actual &&
+               git grep --no-recursive -n -e vvv $H -- t >actual &&
                test_cmp expected actual
        '
 
@@ -353,6 +361,8 @@ do
                        echo ${HC}v:1:vvv
                } >expected &&
                git grep --max-depth 0 -n -e vvv $H -- . t >actual &&
+               test_cmp expected actual &&
+               git grep --no-recursive -n -e vvv $H -- . t >actual &&
                test_cmp expected actual
        '
 
@@ -362,6 +372,8 @@ do
                        echo ${HC}v:1:vvv
                } >expected &&
                git grep --max-depth 0 -n -e vvv $H -- t . >actual &&
+               test_cmp expected actual &&
+               git grep --no-recursive -n -e vvv $H -- t . >actual &&
                test_cmp expected actual
        '
        test_expect_success "grep $L with grep.extendedRegexp=false" '
index 48ec7679b8d9632259c3b319d11ad8eb1acd5ceb..41c09f11f4bf44ece085f952e8782d4dfe3fd169 100755 (executable)
@@ -19,8 +19,10 @@ test_expect_success 'init depot' '
                p4 add file1 &&
                p4 submit -d "change 1" &&
                : >file_to_delete &&
+               : >file_to_move &&
                p4 add file_to_delete &&
-               p4 submit -d "file to delete"
+               p4 add file_to_move &&
+               p4 submit -d "add files to delete"
        )
 '
 
@@ -36,6 +38,8 @@ test_expect_success 'create shelved changelist' '
                echo "new file" >file2 &&
                p4 add file2 &&
                p4 delete file_to_delete &&
+               p4 edit file_to_move &&
+               p4 move file_to_move moved_file &&
                p4 opened &&
                p4 shelve -i <<EOF
 Change: new
@@ -47,6 +51,8 @@ Files:
        //depot/file1
        //depot/file2
        //depot/file_to_delete
+       //depot/file_to_move
+       //depot/moved_file
 EOF
 
        ) &&
@@ -54,12 +60,14 @@ EOF
                cd "$git" &&
                change=$(last_shelved_change) &&
                git p4 unshelve $change &&
-               git show refs/remotes/p4/unshelved/$change | grep -q "Further description" &&
-               git cherry-pick refs/remotes/p4/unshelved/$change &&
+               git show refs/remotes/p4-unshelved/$change | grep -q "Further description" &&
+               git cherry-pick refs/remotes/p4-unshelved/$change &&
                test_path_is_file file2 &&
                test_cmp file1 "$cli"/file1 &&
                test_cmp file2 "$cli"/file2 &&
-               test_path_is_missing file_to_delete
+               test_path_is_missing file_to_delete &&
+               test_path_is_missing file_to_move &&
+               test_path_is_file moved_file
        )
 '
 
@@ -88,10 +96,22 @@ EOF
                cd "$git" &&
                change=$(last_shelved_change) &&
                git p4 unshelve $change &&
-               git diff refs/remotes/p4/unshelved/$change.0 refs/remotes/p4/unshelved/$change | grep -q file3
+               git diff refs/remotes/p4-unshelved/$change.0 refs/remotes/p4-unshelved/$change | grep -q file3
        )
 '
 
+shelve_one_file () {
+       description="Change to be unshelved" &&
+       file="$1" &&
+       p4 shelve -i <<EOF
+Change: new
+Description:
+       $description
+Files:
+       $file
+EOF
+}
+
 # This is the tricky case where the shelved changelist base revision doesn't
 # match git-p4's idea of the base revision
 #
@@ -108,29 +128,52 @@ test_expect_success 'create shelved changelist based on p4 change ahead of p4/ma
                p4 submit -d "change:foo" &&
                p4 edit file1 &&
                echo "bar" >>file1 &&
-               p4 shelve -i <<EOF &&
-Change: new
-Description:
-       Change to be unshelved
-Files:
-       //depot/file1
-EOF
+               shelve_one_file //depot/file1 &&
                change=$(last_shelved_change) &&
-               p4 describe -S $change | grep -q "Change to be unshelved"
+               p4 describe -S $change >out.txt &&
+               grep -q "Change to be unshelved" out.txt
        )
 '
 
-# Now try to unshelve it. git-p4 should refuse to do so.
+# Now try to unshelve it.
 test_expect_success 'try to unshelve the change' '
        test_when_finished cleanup_git &&
        (
                change=$(last_shelved_change) &&
                cd "$git" &&
-               test_must_fail git p4 unshelve $change 2>out.txt &&
-               grep -q "cannot unshelve" out.txt
+               git p4 unshelve $change >out.txt &&
+               grep -q "unshelved changelist $change" out.txt
        )
 '
 
+# Specify the origin. Create 2 unrelated files, and check that
+# we only get the one in HEAD~, not the one in HEAD.
+
+test_expect_success 'unshelve specifying the origin' '
+       (
+               cd "$cli" &&
+               : >unrelated_file0 &&
+               p4 add unrelated_file0 &&
+               p4 submit -d "unrelated" &&
+               : >unrelated_file1 &&
+               p4 add unrelated_file1 &&
+               p4 submit -d "unrelated" &&
+               : >file_to_shelve &&
+               p4 add file_to_shelve &&
+               shelve_one_file //depot/file_to_shelve
+       ) &&
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot/@all &&
+       (
+               cd "$git" &&
+               change=$(last_shelved_change) &&
+               git p4 unshelve --origin HEAD~ $change &&
+               git checkout refs/remotes/p4-unshelved/$change &&
+               test_path_is_file unrelated_file0 &&
+               test_path_is_missing unrelated_file1 &&
+               test_path_is_file file_to_shelve
+       )
+'
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index 3f95bfda605f7ad1f9b0b0385ffdc91e5cca415e..897e6fcc94a195bd8b4493deee411ba9c6ba9f15 100644 (file)
@@ -134,9 +134,40 @@ export EDITOR
 GIT_TRACE_BARE=1
 export GIT_TRACE_BARE
 
-if test -n "${TEST_GIT_INDEX_VERSION:+isset}"
+check_var_migration () {
+       # the warnings and hints given from this helper depends
+       # on end-user settings, which will disrupt the self-test
+       # done on the test framework itself.
+       case "$GIT_TEST_FRAMEWORK_SELFTEST" in
+       t)      return ;;
+       esac
+
+       old_name=$1 new_name=$2
+       eval "old_isset=\${${old_name}:+isset}"
+       eval "new_isset=\${${new_name}:+isset}"
+
+       case "$old_isset,$new_isset" in
+       isset,)
+               echo >&2 "warning: $old_name is now $new_name"
+               echo >&2 "hint: set $new_name too during the transition period"
+               eval "$new_name=\$$old_name"
+               ;;
+       isset,isset)
+               # do this later
+               # echo >&2 "warning: $old_name is now $new_name"
+               # echo >&2 "hint: remove $old_name"
+               ;;
+       esac
+}
+
+check_var_migration GIT_FSMONITOR_TEST GIT_TEST_FSMONITOR
+check_var_migration TEST_GIT_INDEX_VERSION GIT_TEST_INDEX_VERSION
+check_var_migration GIT_FORCE_PRELOAD_TEST GIT_TEST_PRELOAD_INDEX
+
+# Use specific version of the index file format
+if test -n "${GIT_TEST_INDEX_VERSION:+isset}"
 then
-       GIT_INDEX_VERSION="$TEST_GIT_INDEX_VERSION"
+       GIT_INDEX_VERSION="$GIT_TEST_INDEX_VERSION"
        export GIT_INDEX_VERSION
 fi
 
diff --git a/tag.c b/tag.c
index 1db663d71623a493286c90b13532c7d1cf73f517..7445b8f6ea4d371bc76aa5dcc1c1448abef149a1 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -144,7 +144,7 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
                return 0;
        item->object.parsed = 1;
 
-       if (size < GIT_SHA1_HEXSZ + 24)
+       if (size < the_hash_algo->hexsz + 24)
                return -1;
        if (memcmp("object ", bufptr, 7) || parse_oid_hex(bufptr + 7, &oid, &bufptr) || *bufptr++ != '\n')
                return -1;
index 143ca008c8f20384c8c4636e5372817946e66e14..7213fa0d320284e318c7895cc0915ef556ac4f6d 100644 (file)
@@ -1105,6 +1105,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push,
 }
 
 static struct transport_vtable vtable = {
+       0,
        set_helper_option,
        get_refs_list,
        fetch,
index 1cde6258a73bcf8582b0746d1c44a23b30115dc9..004bee5e368f24963d925af997aec051189e1acf 100644 (file)
@@ -6,6 +6,12 @@ struct transport;
 struct argv_array;
 
 struct transport_vtable {
+       /**
+        * This transport supports the fetch() function being called
+        * without get_refs_list() first being called.
+        */
+       unsigned fetch_without_list : 1;
+
        /**
         * Returns 0 if successful, positive if the option is not
         * recognized or is inapplicable, and negative if the option
index 1c76d64aba9fac7d62dfc8e95ab2b32f45dd95a5..01ce11a32575fc8ca889ba99f610983ef150688d 100644 (file)
@@ -252,8 +252,18 @@ static int connect_setup(struct transport *transport, int for_push)
        return 0;
 }
 
-static struct ref *get_refs_via_connect(struct transport *transport, int for_push,
-                                       const struct argv_array *ref_prefixes)
+/*
+ * Obtains the protocol version from the transport and writes it to
+ * transport->data->version, first connecting if not already connected.
+ *
+ * If the protocol version is one that allows skipping the listing of remote
+ * refs, and must_list_refs is 0, the listing of remote refs is skipped and
+ * this function returns NULL. Otherwise, this function returns the list of
+ * remote refs.
+ */
+static struct ref *handshake(struct transport *transport, int for_push,
+                            const struct argv_array *ref_prefixes,
+                            int must_list_refs)
 {
        struct git_transport_data *data = transport->data;
        struct ref *refs = NULL;
@@ -268,8 +278,10 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
        data->version = discover_version(&reader);
        switch (data->version) {
        case protocol_v2:
-               get_remote_refs(data->fd[1], &reader, &refs, for_push,
-                               ref_prefixes, transport->server_options);
+               if (must_list_refs)
+                       get_remote_refs(data->fd[1], &reader, &refs, for_push,
+                                       ref_prefixes,
+                                       transport->server_options);
                break;
        case protocol_v1:
        case protocol_v0:
@@ -283,9 +295,18 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
        }
        data->got_remote_heads = 1;
 
+       if (reader.line_peeked)
+               BUG("buffer must be empty at the end of handshake()");
+
        return refs;
 }
 
+static struct ref *get_refs_via_connect(struct transport *transport, int for_push,
+                                       const struct argv_array *ref_prefixes)
+{
+       return handshake(transport, for_push, ref_prefixes, 1);
+}
+
 static int fetch_refs_via_pack(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
 {
@@ -320,8 +341,17 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.server_options = transport->server_options;
        args.negotiation_tips = data->options.negotiation_tips;
 
-       if (!data->got_remote_heads)
-               refs_tmp = get_refs_via_connect(transport, 0, NULL);
+       if (!data->got_remote_heads) {
+               int i;
+               int must_list_refs = 0;
+               for (i = 0; i < nr_heads; i++) {
+                       if (!to_fetch[i]->exact_oid) {
+                               must_list_refs = 1;
+                               break;
+                       }
+               }
+               refs_tmp = handshake(transport, 0, NULL, must_list_refs);
+       }
 
        switch (data->version) {
        case protocol_v2:
@@ -703,6 +733,7 @@ static int disconnect_git(struct transport *transport)
 }
 
 static struct transport_vtable taken_over_vtable = {
+       1,
        NULL,
        get_refs_via_connect,
        fetch_refs_via_pack,
@@ -852,6 +883,7 @@ void transport_check_allowed(const char *type)
 }
 
 static struct transport_vtable bundle_vtable = {
+       0,
        NULL,
        get_refs_from_bundle,
        fetch_refs_from_bundle,
@@ -861,6 +893,7 @@ static struct transport_vtable bundle_vtable = {
 };
 
 static struct transport_vtable builtin_smart_vtable = {
+       1,
        NULL,
        get_refs_via_connect,
        fetch_refs_via_pack,
@@ -1139,7 +1172,8 @@ int transport_push(struct transport *transport,
                                        oid_array_append(&commits,
                                                          &ref->new_oid);
 
-                       if (!push_unpushed_submodules(&commits,
+                       if (!push_unpushed_submodules(&the_index,
+                                                     &commits,
                                                      transport->remote,
                                                      rs,
                                                      transport->push_options,
@@ -1163,8 +1197,10 @@ int transport_push(struct transport *transport,
                                        oid_array_append(&commits,
                                                          &ref->new_oid);
 
-                       if (find_unpushed_submodules(&commits, transport->remote->name,
-                                               &needs_pushing)) {
+                       if (find_unpushed_submodules(&the_index,
+                                                    &commits,
+                                                    transport->remote->name,
+                                                    &needs_pushing)) {
                                oid_array_clear(&commits);
                                die_with_unpushed_submodules(&needs_pushing);
                        }
@@ -1224,6 +1260,15 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
        struct ref **heads = NULL;
        struct ref *rm;
 
+       if (!transport->vtable->fetch_without_list)
+               /*
+                * Some transports (e.g. the built-in bundle transport and the
+                * transport helper interface) do not work when fetching is
+                * done immediately after transport creation. List the remote
+                * refs anyway (if not already listed) as a workaround.
+                */
+               transport_get_remote_refs(transport, NULL);
+
        for (rm = refs; rm; rm = rm->next) {
                nr_refs++;
                if (rm->peer_ref &&
@@ -1325,6 +1370,33 @@ literal_copy:
        return xstrdup(url);
 }
 
+static void fill_alternate_refs_command(struct child_process *cmd,
+                                       const char *repo_path)
+{
+       const char *value;
+
+       if (!git_config_get_value("core.alternateRefsCommand", &value)) {
+               cmd->use_shell = 1;
+
+               argv_array_push(&cmd->args, value);
+               argv_array_push(&cmd->args, repo_path);
+       } else {
+               cmd->git_cmd = 1;
+
+               argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
+               argv_array_push(&cmd->args, "for-each-ref");
+               argv_array_push(&cmd->args, "--format=%(objectname)");
+
+               if (!git_config_get_value("core.alternateRefsPrefixes", &value)) {
+                       argv_array_push(&cmd->args, "--");
+                       argv_array_split(&cmd->args, value);
+               }
+       }
+
+       cmd->env = local_repo_env;
+       cmd->out = -1;
+}
+
 static void read_alternate_refs(const char *path,
                                alternate_ref_fn *cb,
                                void *data)
@@ -1333,12 +1405,7 @@ static void read_alternate_refs(const char *path,
        struct strbuf line = STRBUF_INIT;
        FILE *fh;
 
-       cmd.git_cmd = 1;
-       argv_array_pushf(&cmd.args, "--git-dir=%s", path);
-       argv_array_push(&cmd.args, "for-each-ref");
-       argv_array_push(&cmd.args, "--format=%(objectname) %(refname)");
-       cmd.env = local_repo_env;
-       cmd.out = -1;
+       fill_alternate_refs_command(&cmd, path);
 
        if (start_command(&cmd))
                return;
@@ -1346,15 +1413,15 @@ static void read_alternate_refs(const char *path,
        fh = xfdopen(cmd.out, "r");
        while (strbuf_getline_lf(&line, fh) != EOF) {
                struct object_id oid;
+               const char *p;
 
-               if (get_oid_hex(line.buf, &oid) ||
-                   line.buf[GIT_SHA1_HEXSZ] != ' ') {
+               if (parse_oid_hex(line.buf, &oid, &p) || *p) {
                        warning(_("invalid line while parsing alternate refs: %s"),
                                line.buf);
                        break;
                }
 
-               cb(line.buf + GIT_SHA1_HEXSZ + 1, &oid, data);
+               cb(&oid, data);
        }
 
        fclose(fh);
index 01e717c29ee6e0f0724b5757ebc7538a72e7e807..9baeca2d7a34cf064a632a7677429f477cb8e014 100644 (file)
@@ -261,6 +261,6 @@ int transport_refs_pushed(struct ref *ref);
 void transport_print_push_status(const char *dest, struct ref *refs,
                  int verbose, int porcelain, unsigned int *reject_reasons);
 
-typedef void alternate_ref_fn(const char *refname, const struct object_id *oid, void *);
+typedef void alternate_ref_fn(const struct object_id *oid, void *);
 extern void for_each_alternate_ref(alternate_ref_fn, void *);
 #endif
index 425668e1e0b468d55e87dba69169acd53bba2c31..0e5432461026eff15fe101cf017cd839702e5870 100644 (file)
@@ -605,7 +605,7 @@ static void try_to_follow_renames(const struct object_id *old_oid,
        choice = q->queue[0];
        q->nr = 0;
 
-       diff_setup(&diff_opts);
+       repo_diff_setup(opt->repo, &diff_opts);
        diff_opts.flags.recursive = 1;
        diff_opts.flags.find_copies_harder = 1;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
index 51bfac6aa0bea246cdf6415490654b7ca2e4b9c2..7570df481bf69824e4b163a6c7a15985b72d1326 100644 (file)
@@ -380,7 +380,7 @@ static int check_updates(struct unpack_trees_options *o)
 {
        unsigned cnt = 0;
        int errs = 0;
-       struct progress *progress = NULL;
+       struct progress *progress;
        struct index_state *index = &o->result;
        struct checkout state = CHECKOUT_INIT;
        int i;
index 14e42526ce4c002ecba9d572d8fc4ff12611d33b..5e81f1ff24f141fc5357522cb0745c7ff0aeb189 100644 (file)
@@ -449,6 +449,7 @@ static int do_reachable_revlist(struct child_process *cmd,
        struct object *o;
        char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */
        int i;
+       const unsigned hexsz = the_hash_algo->hexsz;
 
        cmd->argv = argv;
        cmd->git_cmd = 1;
@@ -467,7 +468,7 @@ static int do_reachable_revlist(struct child_process *cmd,
                goto error;
 
        namebuf[0] = '^';
-       namebuf[GIT_SHA1_HEXSZ + 1] = '\n';
+       namebuf[hexsz + 1] = '\n';
        for (i = get_max_object_index(); 0 < i; ) {
                o = get_indexed_object(--i);
                if (!o)
@@ -476,11 +477,11 @@ static int do_reachable_revlist(struct child_process *cmd,
                        o->flags &= ~TMP_MARK;
                if (!is_our_ref(o))
                        continue;
-               memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
-               if (write_in_full(cmd->in, namebuf, GIT_SHA1_HEXSZ + 2) < 0)
+               memcpy(namebuf + 1, oid_to_hex(&o->oid), hexsz);
+               if (write_in_full(cmd->in, namebuf, hexsz + 2) < 0)
                        goto error;
        }
-       namebuf[GIT_SHA1_HEXSZ] = '\n';
+       namebuf[hexsz] = '\n';
        for (i = 0; i < src->nr; i++) {
                o = src->objects[i].item;
                if (is_our_ref(o)) {
@@ -490,8 +491,8 @@ static int do_reachable_revlist(struct child_process *cmd,
                }
                if (reachable && o->type == OBJ_COMMIT)
                        o->flags |= TMP_MARK;
-               memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
-               if (write_in_full(cmd->in, namebuf, GIT_SHA1_HEXSZ + 1) < 0)
+               memcpy(namebuf, oid_to_hex(&o->oid), hexsz);
+               if (write_in_full(cmd->in, namebuf, hexsz + 1) < 0)
                        goto error;
        }
        close(cmd->in);
@@ -1036,14 +1037,17 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
                keepalive = git_config_int(var, value);
                if (!keepalive)
                        keepalive = -1;
-       } else if (current_config_scope() != CONFIG_SCOPE_REPO) {
-               if (!strcmp("uploadpack.packobjectshook", var))
-                       return git_config_string(&pack_objects_hook, var, value);
        } else if (!strcmp("uploadpack.allowfilter", var)) {
                allow_filter = git_config_bool(var, value);
        } else if (!strcmp("uploadpack.allowrefinwant", var)) {
                allow_ref_in_want = git_config_bool(var, value);
        }
+
+       if (current_config_scope() != CONFIG_SCOPE_REPO) {
+               if (!strcmp("uploadpack.packobjectshook", var))
+                       return git_config_string(&pack_objects_hook, var, value);
+       }
+
        return parse_hide_refs_config(var, value, "uploadpack");
 }
 
index f565f6731d1336c761b527a8ce8a62a21b453dcc..46d34cc2a436c6084531d39628d013785700e2ba 100644 (file)
@@ -270,7 +270,8 @@ struct userdiff_driver *userdiff_find_by_name(const char *name) {
        return userdiff_find_by_namelen(name, len);
 }
 
-struct userdiff_driver *userdiff_find_by_path(const char *path)
+struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
+                                             const char *path)
 {
        static struct attr_check *check;
 
@@ -278,7 +279,7 @@ struct userdiff_driver *userdiff_find_by_path(const char *path)
                check = attr_check_initl("diff", NULL);
        if (!path)
                return NULL;
-       git_check_attr(&the_index, path, check);
+       git_check_attr(istate, path, check);
 
        if (ATTR_TRUE(check->items[0].value))
                return &driver_true;
index 2ef0ce545278ea2f40526c42303c541736643bd5..b072bfe89a46efb47c2808b5469bdf1bbfb4cc63 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "notes-cache.h"
 
+struct index_state;
+
 struct userdiff_funcname {
        const char *pattern;
        int cflags;
@@ -21,7 +23,8 @@ struct userdiff_driver {
 
 int userdiff_config(const char *k, const char *v);
 struct userdiff_driver *userdiff_find_by_name(const char *name);
-struct userdiff_driver *userdiff_find_by_path(const char *path);
+struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
+                                             const char *path);
 
 /*
  * Initialize any textconv-related fields in the driver and return it, or NULL
index 60b79c35b97cbdc9589fe3778ac1f4b5f61646ff..9dcf9337c18b695c2d555f8b96edea0bcb71fd04 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef FAST_EXPORT_H_
-#define FAST_EXPORT_H_
+#ifndef FAST_EXPORT_H
+#define FAST_EXPORT_H
 
 struct strbuf;
 struct line_buffer;
index ee23b4f490258548a7c50fa7c9a50213b70f5359..e192aedea2964e9045f9e71fa95863b9fc03d8c9 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef LINE_BUFFER_H_
-#define LINE_BUFFER_H_
+#ifndef LINE_BUFFER_H
+#define LINE_BUFFER_H
 
 #include "strbuf.h"
 
index b43a825cbabe92b8b9da7d1ff2856763fe26fc29..189c32d84c3c654c0a16bfe71284b24029724019 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef SLIDING_WINDOW_H_
-#define SLIDING_WINDOW_H_
+#ifndef SLIDING_WINDOW_H
+#define SLIDING_WINDOW_H
 
 #include "strbuf.h"
 
index 74eb464bab22eae4af4d1a3228da5964bbc0c568..10a2cbc40ec1c1ec7a97e967e3420b0b819f6641 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef SVNDIFF_H_
-#define SVNDIFF_H_
+#ifndef SVNDIFF_H
+#define SVNDIFF_H
 
 struct line_buffer;
 struct sliding_view;
index b8eb12954e816e0e1490f6ef7eefe64901c38ae1..26faed59684f47038fe1dfe5a43e3f85b06d7582 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef SVNDUMP_H_
-#define SVNDUMP_H_
+#ifndef SVNDUMP_H
+#define SVNDUMP_H
 
 int svndump_init(const char *filename);
 int svndump_init_fd(int in_fd, int back_fd);
diff --git a/ws.c b/ws.c
index a64ab51e09a99e190e5f1912de19b1e093feb360..6e69877f25791632d98bf7b109a2eaebd04c96af 100644 (file)
--- a/ws.c
+++ b/ws.c
@@ -3,7 +3,6 @@
  *
  * Copyright (c) 2007 Junio C Hamano
  */
-
 #include "cache.h"
 #include "attr.h"
 
@@ -71,7 +70,7 @@ unsigned parse_whitespace_rule(const char *string)
        return rule;
 }
 
-unsigned whitespace_rule(const char *pathname)
+unsigned whitespace_rule(struct index_state *istate, const char *pathname)
 {
        static struct attr_check *attr_whitespace_rule;
        const char *value;
@@ -79,7 +78,7 @@ unsigned whitespace_rule(const char *pathname)
        if (!attr_whitespace_rule)
                attr_whitespace_rule = attr_check_initl("whitespace", NULL);
 
-       git_check_attr(&the_index, pathname, attr_whitespace_rule);
+       git_check_attr(istate, pathname, attr_whitespace_rule);
        value = attr_whitespace_rule->items[0].value;
        if (ATTR_TRUE(value)) {
                /* true (whitespace) */
index 1c8746d0ea7d02f6f7899fa5418597b584d8cfb8..187568a112a08c7d2ceda7cdc216b72ebcce6631 100644 (file)
@@ -540,10 +540,12 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
                        /* Leave {mode,oid}_head zero for an add. */
                        d->mode_index = p->two->mode;
                        oidcpy(&d->oid_index, &p->two->oid);
+                       s->committable = 1;
                        break;
                case DIFF_STATUS_DELETED:
                        d->mode_head = p->one->mode;
                        oidcpy(&d->oid_head, &p->one->oid);
+                       s->committable = 1;
                        /* Leave {mode,oid}_index zero for a delete. */
                        break;
 
@@ -561,6 +563,7 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
                        d->mode_index = p->two->mode;
                        oidcpy(&d->oid_head, &p->one->oid);
                        oidcpy(&d->oid_index, &p->two->oid);
+                       s->committable = 1;
                        break;
                case DIFF_STATUS_UNMERGED:
                        d->stagemask = unmerged_mask(p->two->path);
@@ -582,7 +585,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 {
        struct rev_info rev;
 
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        setup_revisions(0, NULL, &rev, NULL);
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
        rev.diffopt.flags.dirty_submodules = 1;
@@ -607,7 +610,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        struct rev_info rev;
        struct setup_revision_opt opt;
 
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        memset(&opt, 0, sizeof(opt));
        opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
        setup_revisions(0, NULL, &rev, &opt);
@@ -665,11 +668,13 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
                         * code will output the stage values directly and not use the
                         * values in these fields.
                         */
+                       s->committable = 1;
                } else {
                        d->index_status = DIFF_STATUS_ADDED;
                        /* Leave {mode,oid}_head zero for adds. */
                        d->mode_index = ce->ce_mode;
                        oidcpy(&d->oid_index, &ce->oid);
+                       s->committable = 1;
                }
        }
 }
@@ -724,15 +729,38 @@ static void wt_status_collect_untracked(struct wt_status *s)
                s->untracked_in_ms = (getnanotime() - t_begin) / 1000000;
 }
 
+static int has_unmerged(struct wt_status *s)
+{
+       int i;
+
+       for (i = 0; i < s->change.nr; i++) {
+               struct wt_status_change_data *d;
+               d = s->change.items[i].util;
+               if (d->stagemask)
+                       return 1;
+       }
+       return 0;
+}
+
 void wt_status_collect(struct wt_status *s)
 {
        wt_status_collect_changes_worktree(s);
-
        if (s->is_initial)
                wt_status_collect_changes_initial(s);
        else
                wt_status_collect_changes_index(s);
        wt_status_collect_untracked(s);
+
+       wt_status_get_state(&s->state, s->branch && !strcmp(s->branch, "HEAD"));
+       if (s->state.merge_in_progress && !has_unmerged(s))
+               s->committable = 1;
+}
+
+void wt_status_collect_free_buffers(struct wt_status *s)
+{
+       free(s->state.branch);
+       free(s->state.onto);
+       free(s->state.detached_from);
 }
 
 static void wt_longstatus_print_unmerged(struct wt_status *s)
@@ -773,7 +801,6 @@ static void wt_longstatus_print_updated(struct wt_status *s)
                        continue;
                if (!shown_header) {
                        wt_longstatus_print_cached_header(s);
-                       s->commitable = 1;
                        shown_header = 1;
                }
                wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it);
@@ -982,7 +1009,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
        int dirty_submodules;
        const char *c = color(WT_STATUS_HEADER, s);
 
-       init_revisions(&rev, NULL);
+       repo_init_revisions(the_repository, &rev, NULL);
        rev.diffopt.flags.allow_textconv = 1;
        rev.diffopt.ita_invisible_in_index = 1;
 
@@ -1008,7 +1035,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
                rev.diffopt.use_color = 0;
                wt_status_add_cut_line(s->fp);
        }
-       if (s->verbose > 1 && s->commitable) {
+       if (s->verbose > 1 && s->committable) {
                /* print_updated() printed a header, so do we */
                if (s->fp != stdout)
                        wt_longstatus_print_trailer(s);
@@ -1063,22 +1090,8 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
        strbuf_release(&sb);
 }
 
-static int has_unmerged(struct wt_status *s)
-{
-       int i;
-
-       for (i = 0; i < s->change.nr; i++) {
-               struct wt_status_change_data *d;
-               d = s->change.items[i].util;
-               if (d->stagemask)
-                       return 1;
-       }
-       return 0;
-}
-
 static void show_merge_in_progress(struct wt_status *s,
-                               struct wt_status_state *state,
-                               const char *color)
+                                  const char *color)
 {
        if (has_unmerged(s)) {
                status_printf_ln(s, color, _("You have unmerged paths."));
@@ -1089,7 +1102,6 @@ static void show_merge_in_progress(struct wt_status *s,
                                         _("  (use \"git merge --abort\" to abort the merge)"));
                }
        } else {
-               s-> commitable = 1;
                status_printf_ln(s, color,
                        _("All conflicts fixed but you are still merging."));
                if (s->hints)
@@ -1100,16 +1112,15 @@ static void show_merge_in_progress(struct wt_status *s,
 }
 
 static void show_am_in_progress(struct wt_status *s,
-                               struct wt_status_state *state,
                                const char *color)
 {
        status_printf_ln(s, color,
                _("You are in the middle of an am session."));
-       if (state->am_empty_patch)
+       if (s->state.am_empty_patch)
                status_printf_ln(s, color,
                        _("The current patch is empty."));
        if (s->hints) {
-               if (!state->am_empty_patch)
+               if (!s->state.am_empty_patch)
                        status_printf_ln(s, color,
                                _("  (fix conflicts and then run \"git am --continue\")"));
                status_printf_ln(s, color,
@@ -1233,10 +1244,9 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines)
 }
 
 static void show_rebase_information(struct wt_status *s,
-                                       struct wt_status_state *state,
-                                       const char *color)
+                                   const char *color)
 {
-       if (state->rebase_interactive_in_progress) {
+       if (s->state.rebase_interactive_in_progress) {
                int i;
                int nr_lines_to_show = 2;
 
@@ -1287,28 +1297,26 @@ static void show_rebase_information(struct wt_status *s,
 }
 
 static void print_rebase_state(struct wt_status *s,
-                               struct wt_status_state *state,
-                               const char *color)
+                              const char *color)
 {
-       if (state->branch)
+       if (s->state.branch)
                status_printf_ln(s, color,
                                 _("You are currently rebasing branch '%s' on '%s'."),
-                                state->branch,
-                                state->onto);
+                                s->state.branch,
+                                s->state.onto);
        else
                status_printf_ln(s, color,
                                 _("You are currently rebasing."));
 }
 
 static void show_rebase_in_progress(struct wt_status *s,
-                               struct wt_status_state *state,
-                               const char *color)
+                                   const char *color)
 {
        struct stat st;
 
-       show_rebase_information(s, state, color);
+       show_rebase_information(s, color);
        if (has_unmerged(s)) {
-               print_rebase_state(s, state, color);
+               print_rebase_state(s, color);
                if (s->hints) {
                        status_printf_ln(s, color,
                                _("  (fix conflicts and then run \"git rebase --continue\")"));
@@ -1317,17 +1325,18 @@ static void show_rebase_in_progress(struct wt_status *s,
                        status_printf_ln(s, color,
                                _("  (use \"git rebase --abort\" to check out the original branch)"));
                }
-       } else if (state->rebase_in_progress || !stat(git_path_merge_msg(the_repository), &st)) {
-               print_rebase_state(s, state, color);
+       } else if (s->state.rebase_in_progress ||
+                  !stat(git_path_merge_msg(the_repository), &st)) {
+               print_rebase_state(s, color);
                if (s->hints)
                        status_printf_ln(s, color,
                                _("  (all conflicts fixed: run \"git rebase --continue\")"));
        } else if (split_commit_in_progress(s)) {
-               if (state->branch)
+               if (s->state.branch)
                        status_printf_ln(s, color,
                                         _("You are currently splitting a commit while rebasing branch '%s' on '%s'."),
-                                        state->branch,
-                                        state->onto);
+                                        s->state.branch,
+                                        s->state.onto);
                else
                        status_printf_ln(s, color,
                                         _("You are currently splitting a commit during a rebase."));
@@ -1335,11 +1344,11 @@ static void show_rebase_in_progress(struct wt_status *s,
                        status_printf_ln(s, color,
                                _("  (Once your working directory is clean, run \"git rebase --continue\")"));
        } else {
-               if (state->branch)
+               if (s->state.branch)
                        status_printf_ln(s, color,
                                         _("You are currently editing a commit while rebasing branch '%s' on '%s'."),
-                                        state->branch,
-                                        state->onto);
+                                        s->state.branch,
+                                        s->state.onto);
                else
                        status_printf_ln(s, color,
                                         _("You are currently editing a commit during a rebase."));
@@ -1354,11 +1363,10 @@ static void show_rebase_in_progress(struct wt_status *s,
 }
 
 static void show_cherry_pick_in_progress(struct wt_status *s,
-                                       struct wt_status_state *state,
-                                       const char *color)
+                                        const char *color)
 {
        status_printf_ln(s, color, _("You are currently cherry-picking commit %s."),
-                       find_unique_abbrev(&state->cherry_pick_head_oid, DEFAULT_ABBREV));
+                       find_unique_abbrev(&s->state.cherry_pick_head_oid, DEFAULT_ABBREV));
        if (s->hints) {
                if (has_unmerged(s))
                        status_printf_ln(s, color,
@@ -1373,11 +1381,10 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
 }
 
 static void show_revert_in_progress(struct wt_status *s,
-                                       struct wt_status_state *state,
-                                       const char *color)
+                                   const char *color)
 {
        status_printf_ln(s, color, _("You are currently reverting commit %s."),
-                        find_unique_abbrev(&state->revert_head_oid, DEFAULT_ABBREV));
+                        find_unique_abbrev(&s->state.revert_head_oid, DEFAULT_ABBREV));
        if (s->hints) {
                if (has_unmerged(s))
                        status_printf_ln(s, color,
@@ -1392,13 +1399,12 @@ static void show_revert_in_progress(struct wt_status *s,
 }
 
 static void show_bisect_in_progress(struct wt_status *s,
-                               struct wt_status_state *state,
-                               const char *color)
+                                   const char *color)
 {
-       if (state->branch)
+       if (s->state.branch)
                status_printf_ln(s, color,
                                 _("You are currently bisecting, started from branch '%s'."),
-                                state->branch);
+                                s->state.branch);
        else
                status_printf_ln(s, color,
                                 _("You are currently bisecting."));
@@ -1572,48 +1578,45 @@ void wt_status_get_state(struct wt_status_state *state,
                wt_status_get_detached_from(state);
 }
 
-static void wt_longstatus_print_state(struct wt_status *s,
-                                     struct wt_status_state *state)
+static void wt_longstatus_print_state(struct wt_status *s)
 {
        const char *state_color = color(WT_STATUS_HEADER, s);
+       struct wt_status_state *state = &s->state;
+
        if (state->merge_in_progress)
-               show_merge_in_progress(s, state, state_color);
+               show_merge_in_progress(s, state_color);
        else if (state->am_in_progress)
-               show_am_in_progress(s, state, state_color);
+               show_am_in_progress(s, state_color);
        else if (state->rebase_in_progress || state->rebase_interactive_in_progress)
-               show_rebase_in_progress(s, state, state_color);
+               show_rebase_in_progress(s, state_color);
        else if (state->cherry_pick_in_progress)
-               show_cherry_pick_in_progress(s, state, state_color);
+               show_cherry_pick_in_progress(s, state_color);
        else if (state->revert_in_progress)
-               show_revert_in_progress(s, state, state_color);
+               show_revert_in_progress(s, state_color);
        if (state->bisect_in_progress)
-               show_bisect_in_progress(s, state, state_color);
+               show_bisect_in_progress(s, state_color);
 }
 
 static void wt_longstatus_print(struct wt_status *s)
 {
        const char *branch_color = color(WT_STATUS_ONBRANCH, s);
        const char *branch_status_color = color(WT_STATUS_HEADER, s);
-       struct wt_status_state state;
-
-       memset(&state, 0, sizeof(state));
-       wt_status_get_state(&state,
-                           s->branch && !strcmp(s->branch, "HEAD"));
 
        if (s->branch) {
                const char *on_what = _("On branch ");
                const char *branch_name = s->branch;
                if (!strcmp(branch_name, "HEAD")) {
                        branch_status_color = color(WT_STATUS_NOBRANCH, s);
-                       if (state.rebase_in_progress || state.rebase_interactive_in_progress) {
-                               if (state.rebase_interactive_in_progress)
+                       if (s->state.rebase_in_progress ||
+                           s->state.rebase_interactive_in_progress) {
+                               if (s->state.rebase_interactive_in_progress)
                                        on_what = _("interactive rebase in progress; onto ");
                                else
                                        on_what = _("rebase in progress; onto ");
-                               branch_name = state.onto;
-                       } else if (state.detached_from) {
-                               branch_name = state.detached_from;
-                               if (state.detached_at)
+                               branch_name = s->state.onto;
+                       } else if (s->state.detached_from) {
+                               branch_name = s->state.detached_from;
+                               if (s->state.detached_at)
                                        on_what = _("HEAD detached at ");
                                else
                                        on_what = _("HEAD detached from ");
@@ -1630,10 +1633,7 @@ static void wt_longstatus_print(struct wt_status *s)
                        wt_longstatus_print_tracking(s);
        }
 
-       wt_longstatus_print_state(s, &state);
-       free(state.branch);
-       free(state.onto);
-       free(state.detached_from);
+       wt_longstatus_print_state(s);
 
        if (s->is_initial) {
                status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
@@ -1665,14 +1665,14 @@ static void wt_longstatus_print(struct wt_status *s)
                                           "new files yourself (see 'git help status')."),
                                         s->untracked_in_ms / 1000.0);
                }
-       } else if (s->commitable)
+       } else if (s->committable)
                status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
                        s->hints
                        ? _(" (use -u option to show untracked files)") : "");
 
        if (s->verbose)
                wt_longstatus_print_verbose(s);
-       if (!s->commitable) {
+       if (!s->committable) {
                if (s->amend)
                        status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes"));
                else if (s->nowarn)
@@ -1937,13 +1937,9 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
        struct branch *branch;
        const char *base;
        const char *branch_name;
-       struct wt_status_state state;
        int ab_info, nr_ahead, nr_behind;
        char eol = s->null_termination ? '\0' : '\n';
 
-       memset(&state, 0, sizeof(state));
-       wt_status_get_state(&state, s->branch && !strcmp(s->branch, "HEAD"));
-
        fprintf(s->fp, "# branch.oid %s%c",
                        (s->is_initial ? "(initial)" : sha1_to_hex(s->sha1_commit)),
                        eol);
@@ -1954,10 +1950,11 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
                if (!strcmp(s->branch, "HEAD")) {
                        fprintf(s->fp, "# branch.head %s%c", "(detached)", eol);
 
-                       if (state.rebase_in_progress || state.rebase_interactive_in_progress)
-                               branch_name = state.onto;
-                       else if (state.detached_from)
-                               branch_name = state.detached_from;
+                       if (s->state.rebase_in_progress ||
+                           s->state.rebase_interactive_in_progress)
+                               branch_name = s->state.onto;
+                       else if (s->state.detached_from)
+                               branch_name = s->state.detached_from;
                        else
                                branch_name = "";
                } else {
@@ -1991,10 +1988,6 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
                        }
                }
        }
-
-       free(state.branch);
-       free(state.onto);
-       free(state.detached_from);
 }
 
 /*
@@ -2314,7 +2307,7 @@ int has_unstaged_changes(int ignore_submodules)
        struct rev_info rev_info;
        int result;
 
-       init_revisions(&rev_info, NULL);
+       repo_init_revisions(the_repository, &rev_info, NULL);
        if (ignore_submodules) {
                rev_info.diffopt.flags.ignore_submodules = 1;
                rev_info.diffopt.flags.override_submodule_config = 1;
@@ -2336,7 +2329,7 @@ int has_uncommitted_changes(int ignore_submodules)
        if (is_cache_unborn())
                return 0;
 
-       init_revisions(&rev_info, NULL);
+       repo_init_revisions(the_repository, &rev_info, NULL);
        if (ignore_submodules)
                rev_info.diffopt.flags.ignore_submodules = 1;
        rev_info.diffopt.flags.quick = 1;
index 1673d146fa2f1e15eabccd1c6b12d0e74b5f0870..1fcf93afbf57a9a6384b6be485846cd35fb3f465 100644 (file)
@@ -64,6 +64,24 @@ enum wt_status_format {
        STATUS_FORMAT_UNSPECIFIED
 };
 
+struct wt_status_state {
+       int merge_in_progress;
+       int am_in_progress;
+       int am_empty_patch;
+       int rebase_in_progress;
+       int rebase_interactive_in_progress;
+       int cherry_pick_in_progress;
+       int bisect_in_progress;
+       int revert_in_progress;
+       int detached_at;
+       char *branch;
+       char *onto;
+       char *detached_from;
+       struct object_id detached_oid;
+       struct object_id revert_head_oid;
+       struct object_id cherry_pick_head_oid;
+};
+
 struct wt_status {
        int is_initial;
        char *branch;
@@ -93,10 +111,11 @@ struct wt_status {
        int rename_score;
        int rename_limit;
        enum wt_status_format status_format;
+       struct wt_status_state state;
        unsigned char sha1_commit[GIT_MAX_RAWSZ]; /* when not Initial */
 
        /* These are computed during processing of the individual sections */
-       int commitable;
+       int committable;
        int workdir_dirty;
        const char *index_file;
        FILE *fp;
@@ -107,29 +126,12 @@ struct wt_status {
        uint32_t untracked_in_ms;
 };
 
-struct wt_status_state {
-       int merge_in_progress;
-       int am_in_progress;
-       int am_empty_patch;
-       int rebase_in_progress;
-       int rebase_interactive_in_progress;
-       int cherry_pick_in_progress;
-       int bisect_in_progress;
-       int revert_in_progress;
-       int detached_at;
-       char *branch;
-       char *onto;
-       char *detached_from;
-       struct object_id detached_oid;
-       struct object_id revert_head_oid;
-       struct object_id cherry_pick_head_oid;
-};
-
 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_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);
 int wt_status_check_rebase(const struct worktree *wt,
                           struct wt_status_state *state);