Merge branch 'jk/cocci-batch'
authorJunio C Hamano <gitster@pobox.com>
Sun, 19 May 2019 07:45:28 +0000 (16:45 +0900)
committerJunio C Hamano <gitster@pobox.com>
Sun, 19 May 2019 07:45:28 +0000 (16:45 +0900)
Optionally "make coccicheck" can feed multiple source files to
spatch, gaining performance while spending more memory.

* jk/cocci-batch:
coccicheck: make batch size of 0 mean "unlimited"
coccicheck: optionally batch spatch invocations

220 files changed:
.gitignore
Documentation/.gitignore
Documentation/Makefile
Documentation/RelNotes/2.22.0.txt
Documentation/config.txt
Documentation/config/advice.txt
Documentation/config/merge.txt
Documentation/config/pack.txt
Documentation/config/repack.txt
Documentation/config/trace2.txt [new file with mode: 0644]
Documentation/fetch-options.txt
Documentation/git-cherry-pick.txt
Documentation/git-clone.txt
Documentation/git-ls-files.txt
Documentation/git-revert.txt
Documentation/git-svn.txt
Documentation/merge-options.txt
Documentation/revisions.txt
Documentation/technical/api-trace2.txt
Documentation/trace2-target-values.txt [new file with mode: 0644]
GIT-VERSION-GEN
Makefile
advice.c
advice.h
archive.c
archive.h
bisect.h
blame.c
blame.h
branch.c
branch.h
builtin.h
builtin/add.c
builtin/clone.c
builtin/commit.c
builtin/merge.c
builtin/pack-objects.c
builtin/pull.c
builtin/rebase--interactive.c [deleted file]
builtin/rebase.c
builtin/repack.c
builtin/replace.c
builtin/rev-list.c
builtin/revert.c
builtin/rm.c
builtin/serve.c [deleted file]
builtin/show-branch.c
builtin/submodule--helper.c
builtin/tag.c
builtin/update-index.c
bulk-checkin.h
cache.h
checkout.h
column.h
commit-graph.c
commit.c
commit.h
common-main.c
compat/access.c [new file with mode: 0644]
compat/fileno.c
compat/mingw.c
compat/win32/trace2_win32_process_info.c
config.c
config.h
config.mak.uname
configure.ac
connect.h
connected.c
connected.h
contrib/coccinelle/commit.cocci
contrib/diff-highlight/DiffHighlight.pm
csum-file.h
decorate.h
delta.h
dir.c
dir.h
exec-cmd.h
fmt-merge-msg.h
fsck.c
fsmonitor.h
generate-cmdlist.sh
gettext.h
git-compat-util.h
git-remote-testgit.sh [deleted file]
git-submodule.sh
git.c
grep.h
hashmap.h
help.h
http.h
khash.h
kwset.h
line-log.c
line-log.h
list-objects.c
lockfile.h
ls-refs.h
mailinfo.h
match-trees.c
merge-blobs.h
merge-recursive.c
notes.c
object-store.h
object.h
oidmap.h
pack.h
packfile.c
packfile.h
parse-options-cb.c
parse-options.h
path.h
pkt-line.h
ppc/sha1.c
prio-queue.h
protocol.h
quote.h
reachable.h
read-cache.c
ref-filter.c
reflog-walk.h
refs.c
refs.h
remote.c
remote.h
replace-object.h
resolve-undo.h
run-command.h
sequencer.c
sequencer.h
serve.h
setup.c
sha1-lookup.h
sha1-name.c
streaming.h
string-list.h
sub-process.h
submodule-config.c
submodule-config.h
t/helper/test-serve-v2.c [new file with mode: 0644]
t/helper/test-tool.c
t/helper/test-tool.h
t/perf/README
t/perf/aggregate.perl
t/perf/p5302-pack-index.sh
t/perf/p5310-pack-bitmaps.sh
t/perf/p5311-pack-bitmaps-fetch.sh
t/perf/p5600-partial-clone.sh [new file with mode: 0755]
t/perf/perf-lib.sh
t/perf/run
t/t0210-trace2-normal.sh
t/t0211-trace2-perf.sh
t/t0212-trace2-event.sh
t/t1007-hash-object.sh
t/t1450-fsck.sh
t/t3009-ls-files-others-nonsubmodule.sh [new file with mode: 0755]
t/t3200-branch.sh
t/t3401-rebase-and-am-rename.sh
t/t3507-cherry-pick-conflict.sh
t/t3511-cherry-pick-x.sh
t/t3700-add.sh
t/t5304-prune.sh
t/t5310-pack-bitmaps.sh
t/t5521-pull-options.sh
t/t5601-clone.sh
t/t5701-git-serve.sh
t/t5702-protocol-v2.sh
t/t5703-upload-pack-ref-in-want.sh
t/t5801-remote-helpers.sh
t/t5801/git-remote-testgit [new file with mode: 0755]
t/t6043-merge-rename-directories.sh
t/t6046-merge-skip-unneeded-updates.sh
t/t6050-replace.sh
t/t6102-rev-list-unexpected-objects.sh [new file with mode: 0755]
t/t6300-for-each-ref.sh
t/t7004-tag.sh
t/t7400-submodule-basic.sh
t/t7502-commit-porcelain.sh
t/t7512-status-help.sh
t/t7600-merge.sh
t/t7604-merge-custom-message.sh
t/t7700-repack.sh
t/t7814-grep-recurse-submodules.sh
t/test-lib-functions.sh
tag.h
tempfile.h
trace.h
trace2.c
trace2.h
trace2/tr2_cfg.c
trace2/tr2_dst.c
trace2/tr2_dst.h
trace2/tr2_sid.c
trace2/tr2_sysenv.c [new file with mode: 0644]
trace2/tr2_sysenv.h [new file with mode: 0644]
trace2/tr2_tbuf.c
trace2/tr2_tbuf.h
trace2/tr2_tgt.h
trace2/tr2_tgt_event.c
trace2/tr2_tgt_normal.c
trace2/tr2_tgt_perf.c
trace2/tr2_tls.c
trace2/tr2_tls.h
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
upload-pack.c
upload-pack.h
url.h
urlmatch.h
utf8.h
varint.h
vcs-svn/sliding_window.h
vcs-svn/svndiff.h
worktree.c
worktree.h
wt-status.c
wt-status.h
xdiff-interface.h
index 44c74402c8c6dedfa24a9f5da6c596c7153b9d25..2374f77a1aae580ed4f71d14e1383bd2dabebd27 100644 (file)
 /git-remote-ftps
 /git-remote-fd
 /git-remote-ext
-/git-remote-testgit
 /git-remote-testpy
 /git-remote-testsvn
 /git-repack
index bf2bf271b5678895e94810dcbdca12b0dfb7de91..9022d4835545cbf40c9537efa8ca9a7678e42673 100644 (file)
@@ -14,3 +14,4 @@ manpage-base-url.xsl
 SubmittingPatches.txt
 tmp-doc-diff/
 GIT-ASCIIDOCFLAGS
+/GIT-EXCLUDED-PROGRAMS
index 6d738f831ebaf316ac79b6a3eb9c544cd18ea7c5..dbf5a0f2762fba5c7d0fcf5f7ec322c413f2b779 100644 (file)
@@ -7,7 +7,10 @@ ARTICLES =
 SP_ARTICLES =
 OBSOLETE_HTML =
 
+-include GIT-EXCLUDED-PROGRAMS
+
 MAN1_TXT += $(filter-out \
+               $(patsubst %,%.txt,$(EXCLUDED_PROGRAMS)) \
                $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
                $(wildcard git-*.txt))
 MAN1_TXT += git.txt
index 85e1122966eca0eed4b102feb7f6630becd1a956..114f147fd63f6a2352cc8a9a0be1afc8dbd69a67 100644 (file)
@@ -67,6 +67,38 @@ UI, Workflows & Features
  * "git submodule" learns "set-branch" subcommand that allows the
    submodule.*.branch settings to be modified.
 
+ * "git merge-recursive" backend recently learned a new heuristics to
+   infer file movement based on how other files in the same directory
+   moved.  As this is inherently less robust heuristics than the one
+   based on the content similarity of the file itself (rather than
+   based on what its neighbours are doing), it sometimes gives an
+   outcome unexpected by the end users.  This has been toned down to
+   leave the renamed paths in higher/conflicted stages in the index so
+   that the user can examine and confirm the result.
+
+ * "git tag" learned to give an advice suggesting it might be a
+   mistake when creating an annotated or signed tag that points at
+   another tag.
+
+ * The "git pack-objects" command learned to report the number of
+   objects it packed via the trace2 mechanism.
+
+ * The list of conflicted paths shown in the editor while concluding a
+   conflicted merge was shown above the scissors line when the
+   clean-up mode is set to "scissors", even though it was commented
+   out just like the list of updated paths and other information to
+   help the user explain the merge better.
+
+ * The trace2 tracing facility learned to auto-generate a filename
+   when told to log to a directory.
+
+ * "git clone" learned a new --server-option option when talking over
+   the protocol version 2.
+
+ * The connectivity bitmaps are created by default in bare
+   repositories now; also the pathname hash-cache is created by
+   default to avoid making crappy deltas when repacking.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -130,6 +162,22 @@ Performance, Internal Implementation, Development Support etc.
    achieve better performance by batching the request for these
    promised blobs.
 
+ * During an initial "git clone --depth=..." partial clone, it is
+   pointless to spend cycles for a large portion of the connectivity
+   check that enumerates and skips promisor objects (which by
+   definition is all objects fetched from the other side).  This has
+   been optimized out.
+
+ * Mechanically and systematically drop "extern" from function
+   declarlation.
+
+ * The script to aggregate perf result unconditionally depended on
+   libjson-perl even though it did not have to, which has been
+   corrected.
+
+ * The internal implementation of "git rebase -i" has been updated to
+   avoid forking a separate "rebase--interactive" process.
+
 
 Fixes since v2.21
 -----------------
@@ -347,10 +395,85 @@ Fixes since v2.21
    eager and considered nonsense strings as if they can be legitimate
    beginning of *-by: trailer.  This has been tightened.
 
- * Build with gettext breaks on recent macOS w/ Homebrew when
-   /usr/local/bin is not on PATH, which has been corrected.
+ * Builds with gettext broke on recent macOS w/ Homebrew, which
+   seems to have stopped including from /usr/local/include; this
+   has been corrected.
    (merge 92a1377a2a js/macos-gettext-build later to maint).
 
+ * Running "git add" on a repository created inside the current
+   repository is an explicit indication that the user wants to add it
+   as a submodule, but when the HEAD of the inner repository is on an
+   unborn branch, it cannot be added as a submodule.  Worse, the files
+   in its working tree can be added as if they are a part of the outer
+   repository, which is not what the user wants.  These problems are
+   being addressed.
+   (merge f937bc2f86 km/empty-repo-is-still-a-repo later to maint).
+
+ * "git cherry-pick" run with the "-x" or the "--signoff" option used
+   to (and more importantly, ought to) clean up the commit log message
+   with the --cleanup=space option by default, but this has been
+   broken since late 2017.  This has been fixed.
+
+ * When given a tag that points at a commit-ish, "git replace --graft"
+   failed to peel the tag before writing a replace ref, which did not
+   make sense because the old graft mechanism the feature wants to
+   mimick only allowed to replace one commit object with another.
+   This has been fixed.
+   (merge ee521ec4cb cc/replace-graft-peel-tags later to maint).
+
+ * Code tightening against a "wrong" object appearing where an object
+   of a different type is expected, instead of blindly assuming that
+   the connection between objects are correctly made.
+   (merge 97dd512af7 tb/unexpected later to maint).
+
+ * An earlier update for MinGW and Cygwin accidentally broke MSVC build,
+   which has been fixed.
+   (merge 22c3634c0f ss/msvc-path-utils-fix later to maint).
+
+ * %(push:track) token used in the --format option to "git
+   for-each-ref" and friends was not showing the right branch, which
+   has been fixed.
+   (merge c646d0934e dr/ref-filter-push-track-fix later to maint).
+
+ * "make check-docs", "git help -a", etc. did not account for cases
+   where a particular build may deliberately omit some subcommands,
+   which has been corrected.
+
+ * The logic to tell if a Git repository has a working tree protects
+   "git branch -D" from removing the branch that is currently checked
+   out by mistake.  The implementation of this logic was broken for
+   repositories with unusual name, which unfortunately is the norm for
+   submodules these days.  This has been fixed.
+   (merge f3534c98e4 jt/submodule-repo-is-with-worktree later to maint).
+
+ * AIX shared the same build issues with other BSDs around fileno(fp),
+   which has been corrected.
+   (merge ee662bf5c6 cc/aix-has-fileno-as-a-macro later to maint).
+
+ * The autoconf generated configure script failed to use the right
+   gettext() implementations from -libintl by ignoring useless stub
+   implementations shipped in some C library, which has been
+   corrected.
+   (merge b71e56a683 vk/autoconf-gettext later to maint).
+
+ * Fix index-pack perf test so that the repeated invocations always
+   run in an empty repository, which emulates the initial clone
+   situation better.
+   (merge 775c71e16d jk/p5302-avoid-collision-check-cost later to maint).
+
+ * A "ls-files" that emulates "find" to enumerate files in the working
+   tree resulted in duplicated Makefile rules that caused the build to
+   issue an unnecessary warning during a trial build after merge
+   conflicts are resolved in working tree *.h files but before the
+   resolved results are added to the index.  This has been corrected.
+
+ * "git chery-pick" (and "revert" that shares the same runtime engine)
+   that deals with multiple commits got confused when the final step
+   gets stopped with a conflict and the user concluded the sequence
+   with "git commit".  Attempt to fix it by cleaning up the state
+   files used by these commands in such a situation.
+   (merge 4a72486de9 pw/clean-sequencer-state-upon-final-commit later to maint).
+
  * Code cleanup, docfix, build fix, etc.
    (merge 11f470aee7 jc/test-yes-doc later to maint).
    (merge 90503a240b js/doc-symref-in-proto-v1 later to maint).
@@ -385,3 +508,5 @@ Fixes since v2.21
    (merge f64a21bd82 tz/doc-apostrophe-no-longer-needed later to maint).
    (merge dbe7b41019 js/t3301-unbreak-notes-test later to maint).
    (merge d8083e4180 km/t3000-retitle later to maint).
+   (merge 9e4cbccbd7 tz/git-svn-doc-markup-fix later to maint).
+   (merge da9ca955a7 jk/ls-files-doc-markup-fix later to maint).
index d87846faa6c3ffad7cba97fe7348ef97e0112899..7e2a6f61f593921450c311b05269a3555baa97ad 100644 (file)
@@ -422,6 +422,8 @@ include::config/submodule.txt[]
 
 include::config/tag.txt[]
 
+include::config/trace2.txt[]
+
 include::config/transfer.txt[]
 
 include::config/uploadarchive.txt[]
index 88620429eacf857c7ba7941314c9b443b35b58ce..ec4f6ae6585bac4b107134e52f544d53c24cdf86 100644 (file)
@@ -90,4 +90,6 @@ advice.*::
        waitingForEditor::
                Print a message to the terminal whenever Git is waiting for
                editor input from the user.
+       nestedTag::
+               Advice shown if a user attempts to recursively tag a tag object.
 --
index d389c739292c2d16e6a0767157f9d4781caa1d29..6a313937f8c024d18188d94abcb797aea6bc3bc6 100644 (file)
@@ -39,9 +39,22 @@ merge.renameLimit::
        is turned off.
 
 merge.renames::
-       Whether and how Git detects renames.  If set to "false",
-       rename detection is disabled. If set to "true", basic rename
-       detection is enabled.  Defaults to the value of diff.renames.
+       Whether Git detects renames.  If set to "false", rename detection
+       is disabled. If set to "true", basic rename detection is enabled.
+       Defaults to the value of diff.renames.
+
+merge.directoryRenames::
+       Whether Git detects directory renames, affecting what happens at
+       merge time to new files added to a directory on one side of
+       history when that directory was renamed on the other side of
+       history.  If merge.directoryRenames is set to "false", directory
+       rename detection is disabled, meaning that such new files will be
+       left behind in the old directory.  If set to "true", directory
+       rename detection is enabled, meaning that such new files will be
+       moved into the new directory.  If set to "conflict", a conflict
+       will be reported for such paths.  If merge.renames is false,
+       merge.directoryRenames is ignored and treated as false.  Defaults
+       to "conflict".
 
 merge.renormalize::
        Tell Git that canonical representation of files in the
index 425c73aa521a67f87941bd8fc11c69b52432cb00..9cdcfa7324784299f431d94b5237cc136aa585d1 100644 (file)
@@ -124,6 +124,4 @@ pack.writeBitmapHashCache::
        bitmapped and non-bitmapped objects (e.g., when serving a fetch
        between an older, bitmapped pack and objects that have been
        pushed since the last gc). The downside is that it consumes 4
-       bytes per object of disk space, and that JGit's bitmap
-       implementation does not understand it, causing it to complain if
-       Git and JGit are used on the same repository. Defaults to false.
+       bytes per object of disk space. Defaults to true.
index a5c37813fdad63791d2be8b09bc2ef128fd4a37e..9c413e177e02c6f28865ed79d84de3c5ccf43dd9 100644 (file)
@@ -24,4 +24,4 @@ repack.writeBitmaps::
        packs created for clones and fetches, at the cost of some disk
        space and extra time spent on the initial repack.  This has
        no effect if multiple packfiles are created.
-       Defaults to false.
+       Defaults to true on bare repos, false otherwise.
diff --git a/Documentation/config/trace2.txt b/Documentation/config/trace2.txt
new file mode 100644 (file)
index 0000000..a5f409c
--- /dev/null
@@ -0,0 +1,56 @@
+Trace2 config settings are only read from the system and global
+config files; repository local and worktree config files and `-c`
+command line arguments are not respected.
+
+trace2.normalTarget::
+       This variable controls the normal target destination.
+       It may be overridden by the `GIT_TR2` environment variable.
+       The following table shows possible values.
+
+trace2.perfTarget::
+       This variable controls the performance target destination.
+       It may be overridden by the `GIT_TR2_PERF` environment variable.
+       The following table shows possible values.
+
+trace2.eventTarget::
+       This variable controls the event target destination.
+       It may be overridden by the `GIT_TR2_EVENT` environment variable.
+       The following table shows possible values.
++
+include::../trace2-target-values.txt[]
+
+trace2.normalBrief::
+       Boolean.  When true `time`, `filename`, and `line` fields are
+       omitted from normal output.  May be overridden by the
+       `GIT_TR2_BRIEF` environment variable.  Defaults to false.
+
+trace2.perfBrief::
+       Boolean.  When true `time`, `filename`, and `line` fields are
+       omitted from PERF output.  May be overridden by the
+       `GIT_TR2_PERF_BRIEF` environment variable.  Defaults to false.
+
+trace2.eventBrief::
+       Boolean.  When true `time`, `filename`, and `line` fields are
+       omitted from event output.  May be overridden by the
+       `GIT_TR2_EVENT_BRIEF` environment variable.  Defaults to false.
+
+trace2.eventNesting::
+       Integer.  Specifies desired depth of nested regions in the
+       event output.  Regions deeper than this value will be
+       omitted.  May be overridden by the `GIT_TR2_EVENT_NESTING`
+       environment variable.  Defaults to 2.
+
+trace2.configParams::
+       A comma-separated list of patterns of "important" config
+       settings that should be recorded in the trace2 output.
+       For example, `core.*,remote.*.url` would cause the trace2
+       output to contain events listing each configured remote.
+       May be overridden by the `GIT_TR2_CONFIG_PARAMS` environment
+       variable.  Unset by default.
+
+trace2.destinationDebug::
+       Boolean.  When true Git will print error messages when a
+       trace target destination cannot be opened for writing.
+       By default, these errors are suppressed and tracing is
+       silently disabled.  May be overridden by the
+       `GIT_TR2_DST_DEBUG` environment variable.
index fa0a3151b3f7e96ee61e65669e057e48ee053aae..91c47752ecdc9c77f660ec1b67bb9b1f393c3cb7 100644 (file)
@@ -216,7 +216,8 @@ endif::git-pull[]
 --server-option=<option>::
        Transmit the given string to the server when communicating using
        protocol version 2.  The given string must not contain a NUL or LF
-       character.
+       character.  The server's handling of server options, including
+       unknown ones, is server-specific.
        When multiple `--server-option=<option>` are given, they are all
        sent to the other side in the order listed on the command line.
 
index d64e72462fed7c7c1cd7f0c5543bcd035ce70247..754b16ce0c9da6a137c9f58de86eb44841ae64d6 100644 (file)
@@ -57,6 +57,13 @@ OPTIONS
        With this option, 'git cherry-pick' will let you edit the commit
        message prior to committing.
 
+--cleanup=<mode>::
+       This option determines how the commit message will be cleaned up before
+       being passed on to the commit machinery. See linkgit:git-commit[1] for more
+       details. In particular, if the '<mode>' is given a value of `scissors`,
+       scissors will be appended to `MERGE_MSG` before being passed on in the case
+       of a conflict.
+
 -x::
        When recording the commit, append a line that says
        "(cherry picked from commit ...)" to the original commit
index 2fd12524f95afef9c688d3402cd2a8b6d2d98c2e..a0f14b51f2654ced2fb7f76078d95050d2730a7d 100644 (file)
@@ -131,6 +131,14 @@ objects from the source repository into a pack in the cloned repository.
        is specified. This flag forces progress status even if the
        standard error stream is not directed to a terminal.
 
+--server-option=<option>::
+       Transmit the given string to the server when communicating using
+       protocol version 2.  The given string must not contain a NUL or LF
+       character.  The server's handling of server options, including
+       unknown ones, is server-specific.
+       When multiple `--server-option=<option>` are given, they are all
+       sent to the other side in the order listed on the command line.
+
 --no-checkout::
 -n::
        No checkout of HEAD is performed after the clone is complete.
index 5298f1bc3052f47e390eee780efe665083744309..8461c0e83e9d535b25755be1d5045cdc30145cb4 100644 (file)
@@ -118,6 +118,7 @@ OPTIONS
        linkgit:git-status[1] `--short` or linkgit:git-diff[1]
        `--name-status` for more user-friendly alternatives.
 +
+--
 This option identifies the file status with the following tags (followed by
 a space) at the start of each line:
 
@@ -128,6 +129,7 @@ a space) at the start of each line:
        C::     modified/changed
        K::     to be killed
        ?::     other
+--
 
 -v::
        Similar to `-t`, but use lowercase letters for files
index 6afccb2f1e29fe588c11e979e0d019edc0596b39..0c82ca5bc0e5a380e8fc441fbb34e12c85cffcab 100644 (file)
@@ -66,6 +66,13 @@ more details.
        With this option, 'git revert' will not start the commit
        message editor.
 
+--cleanup=<mode>::
+       This option determines how the commit message will be cleaned up before
+       being passed on to the commit machinery. See linkgit:git-commit[1] for more
+       details. In particular, if the '<mode>' is given a value of `scissors`,
+       scissors will be appended to `MERGE_MSG` before being passed on in the case
+       of a conflict.
+
 -n::
 --no-commit::
        Usually the command automatically creates some commits with
index 223788fa3ee070b2919a01e1b4eb4cc55043b2af..30711625fdec5ef4a46233b27fcd562a56e773e3 100644 (file)
@@ -1100,10 +1100,10 @@ listed below are allowed:
        tags = tags/*/project-a:refs/remotes/project-a/tags/*
 ------------------------------------------------------------------------
 
-Keep in mind that the '\*' (asterisk) wildcard of the local ref
-(right of the ':') *must* be the farthest right path component;
+Keep in mind that the `*` (asterisk) wildcard of the local ref
+(right of the `:`) *must* be the farthest right path component;
 however the remote wildcard may be anywhere as long as it's an
-independent path component (surrounded by '/' or EOL).   This
+independent path component (surrounded by `/` or EOL).   This
 type of configuration is not automatically created by 'init' and
 should be manually entered with a text-editor or using 'git config'.
 
index 92a7d936c1a8fbcc6746a0ddd127e131edb8f2c1..61876dbc335e240b8842aba3dd4e3fd8a54e574a 100644 (file)
@@ -32,6 +32,13 @@ they run `git merge`. To make it easier to adjust such scripts to the
 updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be
 set to `no` at the beginning of them.
 
+--cleanup=<mode>::
+       This option determines how the merge message will be cleaned up before
+       commiting. See linkgit:git-commit[1] for more details. In addition, if
+       the '<mode>' is given a value of `scissors`, scissors will be appended
+       to `MERGE_MSG` before being passed on to the commit machinery in the
+       case of a merge conflict.
+
 --ff::
        When the merge resolves as a fast-forward, only update the branch
        pointer, without creating a merge commit.  This is the default
index 2337a995eccace3af47e98ed006a2ccbd44a4f4e..82c1e5754e775039f4e858fde64619f8a70e7af5 100644 (file)
@@ -65,7 +65,7 @@ some output processing may assume ref names in UTF-8.
 '@'::
   '@' alone is a shortcut for `HEAD`.
 
-'<refname>@{<date>}', e.g. 'master@\{yesterday\}', 'HEAD@{5 minutes ago}'::
+'[<refname>]@{<date>}', e.g. 'master@\{yesterday\}', 'HEAD@{5 minutes ago}'::
   A ref followed by the suffix '@' with a date specification
   enclosed in a brace
   pair (e.g. '\{yesterday\}', '{1 month 2 weeks 3 days 1 hour 1
@@ -95,7 +95,7 @@ some output processing may assume ref names in UTF-8.
   The construct '@{-<n>}' means the <n>th branch/commit checked out
   before the current one.
 
-'<branchname>@\{upstream\}', e.g. 'master@\{upstream\}', '@\{u\}'::
+'[<branchname>]@\{upstream\}', e.g. 'master@\{upstream\}', '@\{u\}'::
   The suffix '@\{upstream\}' to a branchname (short form '<branchname>@\{u\}')
   refers to the branch that the branch specified by branchname is set to build on
   top of (configured with `branch.<name>.remote` and
@@ -103,7 +103,7 @@ some output processing may assume ref names in UTF-8.
   current one. These suffixes are also accepted when spelled in uppercase, and
   they mean the same thing no matter the case.
 
-'<branchname>@\{push\}', e.g. 'master@\{push\}', '@\{push\}'::
+'[<branchname>]@\{push\}', e.g. 'master@\{push\}', '@\{push\}'::
   The suffix '@\{push}' reports the branch "where we would push to" if
   `git push` were run while `branchname` was checked out (or the current
   `HEAD` if no branchname is specified). Since our push destination is
@@ -131,7 +131,7 @@ from one location and push to another. In a non-triangular workflow,
 This suffix is also accepted when spelled in uppercase, and means the same
 thing no matter the case.
 
-'<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
+'<rev>{caret}[<n>]', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
   A suffix '{caret}' to a revision parameter means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
   '<rev>{caret}'
@@ -139,7 +139,9 @@ thing no matter the case.
   '<rev>{caret}0' means the commit itself and is used when '<rev>' is the
   object name of a tag object that refers to a commit object.
 
-'<rev>{tilde}<n>', e.g. 'master{tilde}3'::
+'<rev>{tilde}[<n>]', e.g. 'HEAD{tilde}, master{tilde}3'::
+  A suffix '{tilde}' to a revision parameter means the first parent of
+  that commit object.
   A suffix '{tilde}<n>' to a revision parameter means the commit
   object that is the <n>th generation ancestor of the named
   commit object, following only the first parents.  I.e. '<rev>{tilde}3' is
@@ -159,12 +161,12 @@ thing no matter the case.
   '<rev>{caret}0'
   is a short-hand for '<rev>{caret}\{commit\}'.
 +
-'rev{caret}\{object\}' can be used to make sure 'rev' names an
-object that exists, without requiring 'rev' to be a tag, and
-without dereferencing 'rev'; because a tag is already an object,
+'<rev>{caret}\{object\}' can be used to make sure '<rev>' names an
+object that exists, without requiring '<rev>' to be a tag, and
+without dereferencing '<rev>'; because a tag is already an object,
 it does not have to be dereferenced even once to get to an object.
 +
-'rev{caret}\{tag\}' can be used to ensure that 'rev' identifies an
+'<rev>{caret}\{tag\}' can be used to ensure that '<rev>' identifies an
 existing tag object.
 
 '<rev>{caret}{}', e.g. 'v0.99.8{caret}{}'::
@@ -194,19 +196,16 @@ existing tag object.
   Depending on the given text, the shell's word splitting rules might
   require additional quoting.
 
-'<rev>:<path>', e.g. 'HEAD:README', ':README', 'master:./README'::
+'<rev>:<path>', e.g. 'HEAD:README', 'master:./README'::
   A suffix ':' followed by a path names the blob or tree
   at the given path in the tree-ish object named by the part
   before the colon.
-  ':path' (with an empty part before the colon)
-  is a special case of the syntax described next: content
-  recorded in the index at the given path.
   A path starting with './' or '../' is relative to the current working directory.
   The given path will be converted to be relative to the working tree's root directory.
   This is most useful to address a blob or tree from a commit or tree that has
   the same tree structure as the working tree.
 
-':<n>:<path>', e.g. ':0:README', ':README'::
+':[<n>:]<path>', e.g. ':0:README', ':README'::
   A colon, optionally followed by a stage number (0 to 3) and a
   colon, followed by a path, names a blob object in the
   index at the given path. A missing stage number (and the colon
@@ -302,7 +301,7 @@ The 'r1{caret}@' notation means all parents of 'r1'.
 The 'r1{caret}!' notation includes commit 'r1' but excludes all of its parents.
 By itself, this notation denotes the single commit 'r1'.
 
-The '<rev>{caret}-<n>' notation includes '<rev>' but excludes the <n>th
+The '<rev>{caret}-[<n>]' notation includes '<rev>' but excludes the <n>th
 parent (i.e. a shorthand for '<rev>{caret}<n>..<rev>'), with '<n>' = 1 if
 not given. This is typically useful for merge commits where you
 can just pass '<commit>{caret}-' to get all the commits in the branch
index 2de565fa3d5a15c4a7179d73b112cd83de0beb09..9e585b8e79fbc70434fb9964159d9ee18757f82c 100644 (file)
@@ -22,21 +22,41 @@ Targets are defined using a VTable allowing easy extension to other
 formats in the future.  This might be used to define a binary format,
 for example.
 
+Trace2 is controlled using `trace2.*` config values in the system and
+global config files and `GIT_TR2*` environment variables.  Trace2 does
+not read from repo local or worktree config files or respect `-c`
+command line config settings.
+
 == Trace2 Targets
 
 Trace2 defines the following set of Trace2 Targets.
 Format details are given in a later section.
 
-`GIT_TR2` (NORMAL)::
+=== The Normal Format Target
+
+The normal format target is a tradition printf format and similar
+to GIT_TRACE format.  This format is enabled with the `GIT_TR`
+environment variable or the `trace2.normalTarget` system or global
+config setting.
+
+For example
 
-       a simple printf format like GIT_TRACE.
-+
 ------------
 $ export GIT_TR2=~/log.normal
 $ git version
 git version 2.20.1.155.g426c96fcdb
 ------------
-+
+
+or
+
+------------
+$ git config --global trace2.normalTarget ~/log.normal
+$ git version
+git version 2.20.1.155.g426c96fcdb
+------------
+
+yields
+
 ------------
 $ cat ~/log.normal
 12:28:42.620009 common-main.c:38                  version 2.20.1.155.g426c96fcdb
@@ -46,76 +66,86 @@ $ cat ~/log.normal
 12:28:42.621250 trace2/tr2_tgt_normal.c:124       atexit elapsed:0.001265 code:0
 ------------
 
-`GIT_TR2_PERF` (PERF)::
+=== The Performance Format Target
+
+The performance format target (PERF) is a column-based format to
+replace GIT_TRACE_PERFORMANCE and is suitable for development and
+testing, possibly to complement tools like gprof.  This format is
+enabled with the `GIT_TR2_PERF` environment variable or the
+`trace2.perfTarget` system or global config setting.
+
+For example
 
-       a column-based format to replace GIT_TRACE_PERFORMANCE suitable for
-       development and testing, possibly to complement tools like gprof.
-+
 ------------
 $ export GIT_TR2_PERF=~/log.perf
 $ git version
 git version 2.20.1.155.g426c96fcdb
 ------------
-+
+
+or
+
+------------
+$ git config --global trace2.perfTarget ~/log.perf
+$ git version
+git version 2.20.1.155.g426c96fcdb
+------------
+
+yields
+
 ------------
 $ cat ~/log.perf
 12:28:42.620675 common-main.c:38                  | d0 | main                     | version      |     |           |           |            | 2.20.1.155.g426c96fcdb
-12:28:42.621001 common-main.c:39                  | d0 | main                     | start        |     |           |           |            | git version
+12:28:42.621001 common-main.c:39                  | d0 | main                     | start        |     |  0.001173 |           |            | git version
 12:28:42.621111 git.c:432                         | d0 | main                     | cmd_name     |     |           |           |            | version (version)
 12:28:42.621225 git.c:662                         | d0 | main                     | exit         |     |  0.001227 |           |            | code:0
 12:28:42.621259 trace2/tr2_tgt_perf.c:211         | d0 | main                     | atexit       |     |  0.001265 |           |            | code:0
 ------------
 
-`GIT_TR2_EVENT` (EVENT)::
+=== The Event Format Target
+
+The event format target is a JSON-based format of event data suitable
+for telemetry analysis.  This format is enabled with the `GIT_TR2_EVENT`
+environment variable or the `trace2.eventTarget` system or global config
+setting.
+
+For example
 
-       a JSON-based format of event data suitable for telemetry analysis.
-+
 ------------
 $ export GIT_TR2_EVENT=~/log.event
 $ git version
 git version 2.20.1.155.g426c96fcdb
 ------------
-+
-------------
-$ cat ~/log.event
-{"event":"version","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.620713","file":"common-main.c","line":38,"evt":"1","exe":"2.20.1.155.g426c96fcdb"}
-{"event":"start","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621027","file":"common-main.c","line":39,"argv":["git","version"]}
-{"event":"cmd_name","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621122","file":"git.c","line":432,"name":"version","hierarchy":"version"}
-{"event":"exit","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621236","file":"git.c","line":662,"t_abs":0.001227,"code":0}
-{"event":"atexit","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621268","file":"trace2/tr2_tgt_event.c","line":163,"t_abs":0.001265,"code":0}
-------------
 
-== Enabling a Target
+or
 
-A Trace2 Target is enabled when the corresponding environment variable
-(`GIT_TR2`, `GIT_TR2_PERF`, or `GIT_TR2_EVENT`) is set.  The following
-values are recognized.
-
-`0`::
-`false`::
-
-       Disables the target.
-
-`1`::
-`true`::
-
-       Enables the target and writes stream to `STDERR`.
+------------
+$ git config --global trace2.eventTarget ~/log.event
+$ git version
+git version 2.20.1.155.g426c96fcdb
+------------
 
-`[2-9]`::
+yields
 
-       Enables the target and writes to the already opened file descriptor.
+------------
+$ cat ~/log.event
+{"event":"version","sid":"sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.620713Z","file":"common-main.c","line":38,"evt":"1","exe":"2.20.1.155.g426c96fcdb"}
+{"event":"start","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621027Z","file":"common-main.c","line":39,"t_abs":0.001173,"argv":["git","version"]}
+{"event":"cmd_name","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621122Z","file":"git.c","line":432,"name":"version","hierarchy":"version"}
+{"event":"exit","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621236Z","file":"git.c","line":662,"t_abs":0.001227,"code":0}
+{"event":"atexit","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621268Z","file":"trace2/tr2_tgt_event.c","line":163,"t_abs":0.001265,"code":0}
+------------
 
-`<absolute-pathname>`::
+=== Enabling a Target
 
-       Enables the target, opens and writes to the file in append mode.
+To enable a target, set the corresponding environment variable or
+system or global config value to one of the following:
 
-`af_unix:[<socket_type>:]<absolute-pathname>`::
+include::../trace2-target-values.txt[]
 
-       Enables the target, opens and writes to a Unix Domain Socket
-       (on platforms that support them).
-+
-Socket type can be either `stream` or `dgram`.  If the socket type is
-omitted, Git will try both.
+If the target already exists and is a directory, the traces will be
+written to files (one per process) underneath the given directory. They
+will be named according to the last component of the SID (optionally
+followed by a counter to avoid filename collisions).
 
 == Trace2 API
 
@@ -160,17 +190,23 @@ purposes.
 
 These are concerned with the lifetime of the overall git process.
 
+`void trace2_initialize_clock()`::
+
+       Initialize the Trace2 start clock and nothing else.  This should
+       be called at the very top of main() to capture the process start
+       time and reduce startup order dependencies.
+
 `void trace2_initialize()`::
 
        Determines if any Trace2 Targets should be enabled and
-       initializes the Trace2 facility.  This includes starting the
-       elapsed time clocks and thread local storage (TLS).
+       initializes the Trace2 facility.  This includes setting up the
+       Trace2 thread local storage (TLS).
 +
 This function emits a "version" message containing the version of git
 and the Trace2 protocol.
 +
 This function should be called from `main()` as early as possible in
-the life of the process.
+the life of the process after essential process initialization.
 
 `int trace2_is_enabled()`::
 
@@ -237,15 +273,16 @@ significantly affects program performance or behavior, such as
        Emits a "def_param" messages for "important" configuration
        settings.
 +
-The environment variable `GIT_TR2_CONFIG_PARAMS` can be set to a
+The environment variable `GIT_TR2_CONFIG_PARAMS` or the `trace2.configParams`
+config value can be set to a
 list of patterns of important configuration settings, for example:
 `core.*,remote.*.url`.  This function will iterate over all config
 settings and emit a "def_param" message for each match.
 
 `void trace2_cmd_set_config(const char *key, const char *value)`::
 
-       Emits a "def_param" message for a specific configuration
-       setting IFF it matches the `GIT_TR2_CONFIG_PARAMS` pattern.
+       Emits a "def_param" message for a new or updated key/value
+       pair IF `key` is considered important.
 +
 This is used to hook into `git_config_set()` and catch any
 configuration changes and update a value previously reported by
@@ -412,9 +449,6 @@ recursive tree walk.
 
 === NORMAL Format
 
-NORMAL format is enabled when the `GIT_TR2` environment variable is
-set.
-
 Events are written as lines of the form:
 
 ------------
@@ -431,8 +465,8 @@ Events are written as lines of the form:
 Note that this may contain embedded LF or CRLF characters that are
 not escaped, so the event may spill across multiple lines.
 
-If `GIT_TR2_BRIEF` is true, the `time`, `filename`, and `line` fields
-are omitted.
+If `GIT_TR2_BRIEF` or `trace2.normalBrief` is true, the `time`, `filename`,
+and `line` fields are omitted.
 
 This target is intended to be more of a summary (like GIT_TRACE) and
 less detailed than the other targets.  It ignores thread, region, and
@@ -440,9 +474,6 @@ data messages, for example.
 
 === PERF Format
 
-PERF format is enabled when the `GIT_TR2_PERF` environment variable
-is set.
-
 Events are written as lines of the form:
 
 ------------
@@ -502,8 +533,8 @@ This field is in anticipation of in-proc submodules in the future.
 15:33:33.532712 wt-status.c:2331                  | d0 | main                     | region_leave | r1  |  0.127568 |  0.001504 | status     | label:print
 ------------
 
-If `GIT_TR2_PERF_BRIEF` is true, the `time`, `file`, and `line`
-fields are omitted.
+If `GIT_TR2_PERF_BRIEF` or `trace2.perfBrief` is true, the `time`, `file`,
+and `line` fields are omitted.
 
 ------------
 d0 | main                     | region_leave | r1  |  0.011717 |  0.009122 | index      | label:preload
@@ -514,9 +545,6 @@ during development and is quite noisy.
 
 === EVENT Format
 
-EVENT format is enabled when the `GIT_TR2_EVENT` environment
-variable is set.
-
 Each event is a JSON-object containing multiple key/value pairs
 written as a single line and followed by a LF.
 
@@ -534,11 +562,11 @@ The following key/value pairs are common to all events:
 ------------
 {
        "event":"version",
-       "sid":"1547659722619736-11614",
+       "sid":"20190408T191827.272759Z-H9b68c35f-P00003510",
        "thread":"main",
-       "time":"2019-01-16 17:28:42.620713",
+       "time":"2019-04-08T19:18:27.282761Z",
        "file":"common-main.c",
-       "line":38,
+       "line":42,
        ...
 }
 ------------
@@ -570,9 +598,9 @@ The following key/value pairs are common to all events:
 `"repo":<repo-id>`::
        when present, is the integer repo-id as described previously.
 
-If `GIT_TR2_EVENT_BRIEF` is true, the `file` and `line` fields are omitted
-from all events and the `time` field is only present on the "start" and
-"atexit" events.
+If `GIT_TR2_EVENT_BRIEF` or `trace2.eventBrief` is true, the `file`
+and `line` fields are omitted from all events and the `time` field is
+only present on the "start" and "atexit" events.
 
 ==== Event-Specific Key/Value Pairs
 
@@ -595,6 +623,7 @@ from all events and the `time` field is only present on the "start" and
 {
        "event":"start",
        ...
+       "t_abs":0.001227, # elapsed time in seconds
        "argv":["git","version"]
 }
 ------------
@@ -882,7 +911,7 @@ visited.
 The `category` field may be used in a future enhancement to
 do category-based filtering.
 +
-The `GIT_TR2_EVENT_NESTING` environment variable can be used to
+`GIT_TR2_EVENT_NESTING` or `trace2.eventNesting` can be used to
 filter deeply nested regions and data events.  It defaults to "2".
 
 `"region_leave"`::
@@ -1112,7 +1141,7 @@ $ git status
 
 $ cat ~/log.perf
 d0 | main                     | version      |     |           |           |            | 2.20.1.160.g5676107ecd.dirty
-d0 | main                     | start        |     |           |           |            | git status
+d0 | main                     | start        |     |  0.001173 |           |            | git status
 d0 | main                     | def_repo     | r1  |           |           |            | worktree:/Users/jeffhost/work/gfw
 d0 | main                     | cmd_name     |     |           |           |            | status (status)
 ...
@@ -1157,7 +1186,7 @@ $ git status
 ...
 $ cat ~/log.perf
 d0 | main                     | version      |     |           |           |            | 2.20.1.162.gb4ccea44db.dirty
-d0 | main                     | start        |     |           |           |            | git status
+d0 | main                     | start        |     |  0.001173 |           |            | git status
 d0 | main                     | def_repo     | r1  |           |           |            | worktree:/Users/jeffhost/work/gfw
 d0 | main                     | cmd_name     |     |           |           |            | status (status)
 ...
@@ -1213,7 +1242,7 @@ $ git status
 ...
 $ cat ~/log.perf
 d0 | main                     | version      |     |           |           |            | 2.20.1.156.gf9916ae094.dirty
-d0 | main                     | start        |     |           |           |            | git status
+d0 | main                     | start        |     |  0.001173 |           |            | git status
 d0 | main                     | def_repo     | r1  |           |           |            | worktree:/Users/jeffhost/work/gfw
 d0 | main                     | cmd_name     |     |           |           |            | status (status)
 d0 | main                     | region_enter | r1  |  0.001791 |           | index      | label:do_read_index .git/index
diff --git a/Documentation/trace2-target-values.txt b/Documentation/trace2-target-values.txt
new file mode 100644 (file)
index 0000000..27d3c64
--- /dev/null
@@ -0,0 +1,10 @@
+--
+* `0` or `false` - Disables the target.
+* `1` or `true` - Writes to `STDERR`.
+* `[2-9]` - Writes to the already opened file descriptor.
+* `<absolute-pathname>` - Writes to the file in append mode.
+* `af_unix:[<socket_type>:]<absolute-pathname>` - Write to a
+Unix DomainSocket (on platforms that support them).  Socket
+type can be either `stream` or `dgram`; if omitted Git will
+try both.
+--
index 3fc4065da2ecdb7176766f1c5f5b9243cf4ab718..8a5009caf98c9ce87e794b89f398c64e61d2bb98 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.21.GIT
+DEF_VER=v2.22.0-rc0
 
 LF='
 '
index 9cea614523fc319111af5aba9381788de87f4f0c..8a7e2353520ddd7e0c8074d2b32d0441d97c1597 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -439,6 +439,9 @@ all::
 #
 # Define FILENO_IS_A_MACRO if fileno() is a macro, not a real function.
 #
+# Define NEED_ACCESS_ROOT_HANDLER if access() under root may success for X_OK
+# even if execution permission isn't granted for any user.
+#
 # Define PAGER_ENV to a SP separated VAR=VAL pairs to define
 # default environment variables to be passed when a pager is spawned, e.g.
 #
@@ -592,6 +595,7 @@ FUZZ_PROGRAMS =
 LIB_OBJS =
 PROGRAM_OBJS =
 PROGRAMS =
+EXCLUDED_PROGRAMS =
 SCRIPT_PERL =
 SCRIPT_PYTHON =
 SCRIPT_SH =
@@ -614,7 +618,6 @@ SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-legacy-stash.sh
-SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
@@ -637,17 +640,11 @@ SCRIPT_PERL += git-svn.perl
 
 SCRIPT_PYTHON += git-p4.py
 
-NO_INSTALL += git-remote-testgit
-
 # Generated files for scripts
 SCRIPT_SH_GEN = $(patsubst %.sh,%,$(SCRIPT_SH))
 SCRIPT_PERL_GEN = $(patsubst %.perl,%,$(SCRIPT_PERL))
 SCRIPT_PYTHON_GEN = $(patsubst %.py,%,$(SCRIPT_PYTHON))
 
-SCRIPT_SH_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_SH_GEN))
-SCRIPT_PERL_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PERL_GEN))
-SCRIPT_PYTHON_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PYTHON_GEN))
-
 # Individual rules to allow e.g.
 # "make -C ../.. SCRIPT_PERL=contrib/foo/bar.perl build-perl-script"
 # from subdirectories like contrib/*/
@@ -657,11 +654,11 @@ build-sh-script: $(SCRIPT_SH_GEN)
 build-python-script: $(SCRIPT_PYTHON_GEN)
 
 .PHONY: install-perl-script install-sh-script install-python-script
-install-sh-script: $(SCRIPT_SH_INS)
+install-sh-script: $(SCRIPT_SH_GEN)
        $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-install-perl-script: $(SCRIPT_PERL_INS)
+install-perl-script: $(SCRIPT_PERL_GEN)
        $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-install-python-script: $(SCRIPT_PYTHON_INS)
+install-python-script: $(SCRIPT_PYTHON_GEN)
        $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 
 .PHONY: clean-perl-script clean-sh-script clean-python-script
@@ -672,9 +669,9 @@ clean-perl-script:
 clean-python-script:
        $(RM) $(SCRIPT_PYTHON_GEN)
 
-SCRIPTS = $(SCRIPT_SH_INS) \
-         $(SCRIPT_PERL_INS) \
-         $(SCRIPT_PYTHON_INS) \
+SCRIPTS = $(SCRIPT_SH_GEN) \
+         $(SCRIPT_PERL_GEN) \
+         $(SCRIPT_PYTHON_GEN) \
          git-instaweb
 
 ETAGS_TARGET = TAGS
@@ -744,6 +741,7 @@ TEST_BUILTINS_OBJS += test-repository.o
 TEST_BUILTINS_OBJS += test-revision-walking.o
 TEST_BUILTINS_OBJS += test-run-command.o
 TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
+TEST_BUILTINS_OBJS += test-serve-v2.o
 TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha1-array.o
 TEST_BUILTINS_OBJS += test-sha256.o
@@ -822,12 +820,12 @@ VCSSVN_LIB = vcs-svn/lib.a
 
 GENERATED_H += command-list.h
 
-LIB_H := $(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
+LIB_H := $(sort $(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
        $(FIND) . \
        -name .git -prune -o \
        -name t -prune -o \
        -name Documentation -prune -o \
-       -name '*.h' -print)
+       -name '*.h' -print))
 
 LIB_OBJS += abspath.o
 LIB_OBJS += advice.o
@@ -1004,6 +1002,7 @@ LIB_OBJS += trace2/tr2_cfg.o
 LIB_OBJS += trace2/tr2_cmd_name.o
 LIB_OBJS += trace2/tr2_dst.o
 LIB_OBJS += trace2/tr2_sid.o
+LIB_OBJS += trace2/tr2_sysenv.o
 LIB_OBJS += trace2/tr2_tbuf.o
 LIB_OBJS += trace2/tr2_tgt_event.o
 LIB_OBJS += trace2/tr2_tgt_normal.o
@@ -1110,7 +1109,6 @@ BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/range-diff.o
 BUILTIN_OBJS += builtin/read-tree.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
@@ -1125,7 +1123,6 @@ BUILTIN_OBJS += builtin/rev-parse.o
 BUILTIN_OBJS += builtin/revert.o
 BUILTIN_OBJS += builtin/rm.o
 BUILTIN_OBJS += builtin/send-pack.o
-BUILTIN_OBJS += builtin/serve.o
 BUILTIN_OBJS += builtin/shortlog.o
 BUILTIN_OBJS += builtin/show-branch.o
 BUILTIN_OBJS += builtin/show-index.o
@@ -1345,6 +1342,7 @@ ifdef NO_CURL
        REMOTE_CURL_PRIMARY =
        REMOTE_CURL_ALIASES =
        REMOTE_CURL_NAMES =
+       EXCLUDED_PROGRAMS += git-http-fetch git-http-push
 else
        ifdef CURLDIR
                # Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
@@ -1369,7 +1367,11 @@ endif
        ifeq "$(curl_check)" "070908"
                ifndef NO_EXPAT
                        PROGRAM_OBJS += http-push.o
+               else
+                       EXCLUDED_PROGRAMS += git-http-push
                endif
+       else
+               EXCLUDED_PROGRAMS += git-http-push
        endif
        curl_check := $(shell (echo 072200; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "072200"
@@ -1617,6 +1619,7 @@ ifdef NO_INET_PTON
 endif
 ifdef NO_UNIX_SOCKETS
        BASIC_CFLAGS += -DNO_UNIX_SOCKETS
+       EXCLUDED_PROGRAMS += git-credential-cache git-credential-cache--daemon
 else
        LIB_OBJS += unix-socket.o
        PROGRAM_OBJS += credential-cache.o
@@ -1836,6 +1839,11 @@ ifdef FILENO_IS_A_MACRO
        COMPAT_OBJS += compat/fileno.o
 endif
 
+ifdef NEED_ACCESS_ROOT_HANDLER
+       COMPAT_CFLAGS += -DNEED_ACCESS_ROOT_HANDLER
+       COMPAT_OBJS += compat/access.o
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK = NoThanks
 endif
@@ -2136,7 +2144,9 @@ $(BUILT_INS): git$X
 command-list.h: generate-cmdlist.sh command-list.txt
 
 command-list.h: $(wildcard Documentation/git*.txt) Documentation/*config.txt Documentation/config/*.txt
-       $(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@
+       $(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh \
+               $(patsubst %,--exclude-program %,$(EXCLUDED_PROGRAMS)) \
+               command-list.txt >$@+ && mv $@+ $@
 
 SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
        $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
@@ -2469,6 +2479,14 @@ $(VCSSVN_LIB): $(VCSSVN_OBJS)
 
 export DEFAULT_EDITOR DEFAULT_PAGER
 
+Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
+       @EXCLUDED='EXCLUDED_PROGRAMS := $(EXCLUDED_PROGRAMS)'; \
+           if test x"$$EXCLUDED" != \
+               x"`cat Documentation/GIT-EXCLUDED-PROGRAMS 2>/dev/null`" ; then \
+               echo >&2 "    * new documentation flags"; \
+               echo "$$EXCLUDED" >Documentation/GIT-EXCLUDED-PROGRAMS; \
+            fi
+
 .PHONY: doc man man-perl html info pdf
 doc: man-perl
        $(MAKE) -C Documentation all
@@ -2707,7 +2725,6 @@ endif
 test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 
 all:: $(TEST_PROGRAMS) $(test_bindir_programs)
-all:: $(NO_INSTALL)
 
 bin-wrappers/%: wrap-for-bin.sh
        @mkdir -p bin-wrappers
@@ -2996,7 +3013,7 @@ rpm::
 
 artifacts-tar:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) \
                GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
-               $(NO_INSTALL) $(MOFILES)
+               $(MOFILES)
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \
                SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
        test -n "$(ARTIFACTS_DIRECTORY)"
@@ -3045,7 +3062,7 @@ clean: profile-clean coverage-clean cocciclean
        $(RM) $(OBJECTS)
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
-       $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
+       $(RM) $(TEST_PROGRAMS)
        $(RM) $(FUZZ_PROGRAMS)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
@@ -3054,6 +3071,7 @@ clean: profile-clean coverage-clean cocciclean
        $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
        $(MAKE) -C Documentation/ clean
+       $(RM) Documentation/GIT-EXCLUDED-PROGRAMS
 ifndef NO_PERL
        $(MAKE) -C gitweb clean
        $(RM) -r perl/build/
@@ -3089,7 +3107,7 @@ check-docs::
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
                git-merge-resolve | git-merge-subtree | \
                git-fsck-objects | git-init-db | \
-               git-remote-* | git-stage | \
+               git-remote-* | git-stage | git-legacy-* | \
                git-?*--?* ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
@@ -3113,7 +3131,7 @@ check-docs::
                    -e 's/\.txt//'; \
        ) | while read how cmd; \
        do \
-               case " $(patsubst %$X,%,$(ALL_COMMANDS)) " in \
+               case " $(patsubst %$X,%,$(ALL_COMMANDS) $(EXCLUDED_PROGRAMS)) " in \
                *" $$cmd "*)    ;; \
                *) echo "removed but $$how: $$cmd" ;; \
                esac; \
index 567209aa79afee0443dd0d7049343e1a004ae784..ce5f374ecd4917346145c8a380ac8c0e45e849a4 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -26,6 +26,7 @@ int advice_ignored_hook = 1;
 int advice_waiting_for_editor = 1;
 int advice_graft_file_deprecated = 1;
 int advice_checkout_ambiguous_remote_branch_name = 1;
+int advice_nested_tag = 1;
 
 static int advice_use_color = -1;
 static char advice_colors[][COLOR_MAXLEN] = {
@@ -81,6 +82,7 @@ static struct {
        { "waitingForEditor", &advice_waiting_for_editor },
        { "graftFileDeprecated", &advice_graft_file_deprecated },
        { "checkoutAmbiguousRemoteBranchName", &advice_checkout_ambiguous_remote_branch_name },
+       { "nestedTag", &advice_nested_tag },
 
        /* make this an alias for backward compatibility */
        { "pushNonFastForward", &advice_push_update_rejected }
index f875f8cd8da5fdb6e570770ecc7ab4461745f827..e50f02cdfeff788910699f2d34fff1c93081c24b 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -26,12 +26,13 @@ extern int advice_ignored_hook;
 extern int advice_waiting_for_editor;
 extern int advice_graft_file_deprecated;
 extern int advice_checkout_ambiguous_remote_branch_name;
+extern int advice_nested_tag;
 
 int git_default_advice_config(const char *var, const char *value);
 __attribute__((format (printf, 1, 2)))
 void advise(const char *advice, ...);
 int error_resolve_conflict(const char *me);
-extern void NORETURN die_resolve_conflict(const char *me);
+void NORETURN die_resolve_conflict(const char *me);
 void NORETURN die_conclude_merge(void);
 void detach_advice(const char *new_name);
 
index f2c78a2712b15505be7e0dae2e635b89c97082b8..53141c1f0ee12b4ce14efef1e4026d7ba7d2b665 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -415,7 +415,7 @@ static void parse_treeish_arg(const char **argv,
 
        if (prefix) {
                struct object_id tree_oid;
-               unsigned int mode;
+               unsigned short mode;
                int err;
 
                err = get_tree_entry(&tree->object.oid, prefix, &tree_oid,
index dd022a6b46bc339d19b1c49ebc20214dd137685f..e60e3dd31c79f1c04b7afcae8eb5623238e1234f 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -23,9 +23,9 @@ struct archiver_args {
 
 /* main api */
 
-extern int write_archive(int argc, const char **argv, const char *prefix,
-                        struct repository *repo,
-                        const char *name_hint, int remote);
+int write_archive(int argc, const char **argv, const char *prefix,
+                 struct repository *repo,
+                 const char *name_hint, int remote);
 
 const char *archive_format_from_filename(const char *filename);
 
@@ -39,21 +39,21 @@ struct archiver {
        unsigned flags;
        void *data;
 };
-extern void register_archiver(struct archiver *);
+void register_archiver(struct archiver *);
 
-extern void init_tar_archiver(void);
-extern void init_zip_archiver(void);
-extern void init_archivers(void);
+void init_tar_archiver(void);
+void init_zip_archiver(void);
+void init_archivers(void);
 
 typedef int (*write_archive_entry_fn_t)(struct archiver_args *args,
                                        const struct object_id *oid,
                                        const char *path, size_t pathlen,
                                        unsigned int mode);
 
-extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry);
-extern void *object_file_to_archive(const struct archiver_args *args,
-                                   const char *path, const struct object_id *oid,
-                                   unsigned int mode, enum object_type *type,
-                                   unsigned long *sizep);
+int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry);
+void *object_file_to_archive(const struct archiver_args *args,
+                            const char *path, const struct object_id *oid,
+                            unsigned int mode, enum object_type *type,
+                            unsigned long *sizep);
 
 #endif /* ARCHIVE_H */
index 821d16e4ddad71d177190a0e05012c5a509fa17b..4e69a11ea8f3c60fe0c273fc4af98760fd236f09 100644 (file)
--- a/bisect.h
+++ b/bisect.h
@@ -11,14 +11,14 @@ struct repository;
  * Otherwise, it will be either all non-SAMETREE commits or the single
  * best commit, as chosen by `find_all`.
  */
-extern void find_bisection(struct commit_list **list, int *reaches, int *all,
-                          int find_all);
+void find_bisection(struct commit_list **list, int *reaches, int *all,
+                   int find_all);
 
-extern struct commit_list *filter_skipped(struct commit_list *list,
-                                         struct commit_list **tried,
-                                         int show_all,
-                                         int *count,
-                                         int *skipped_first);
+struct commit_list *filter_skipped(struct commit_list *list,
+                                  struct commit_list **tried,
+                                  int show_all,
+                                  int *count,
+                                  int *skipped_first);
 
 #define BISECT_SHOW_ALL                (1<<0)
 #define REV_LIST_QUIET         (1<<1)
@@ -31,14 +31,14 @@ struct rev_list_info {
        const char *header_prefix;
 };
 
-extern int bisect_next_all(struct repository *r,
-                          const char *prefix,
-                          int no_checkout);
+int bisect_next_all(struct repository *r,
+                   const char *prefix,
+                   int no_checkout);
 
-extern int estimate_bisect_steps(int all);
+int estimate_bisect_steps(int all);
 
-extern void read_bisect_terms(const char **bad, const char **good);
+void read_bisect_terms(const char **bad, const char **good);
 
-extern int bisect_clean_state(void);
+int bisect_clean_state(void);
 
 #endif
diff --git a/blame.c b/blame.c
index c11c516921e990e1b0fa752e77a1438e92697d5e..145eaf2faf9cf56977da61572c93783ea702b0f9 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -99,7 +99,7 @@ static void verify_working_tree_path(struct repository *r,
        for (parents = work_tree->parents; parents; parents = parents->next) {
                const struct object_id *commit_oid = &parents->item->object.oid;
                struct object_id blob_oid;
-               unsigned mode;
+               unsigned short mode;
 
                if (!get_tree_entry(commit_oid, path, &blob_oid, &mode) &&
                    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)
diff --git a/blame.h b/blame.h
index be3a895043e07a2508d407ac35e74d634acd7886..d62f80fa74c44011f8cdaea7a4baec3d8cae03e6 100644 (file)
--- a/blame.h
+++ b/blame.h
@@ -52,7 +52,7 @@ struct blame_origin {
        struct blame_entry *suspects;
        mmfile_t file;
        struct object_id blob_oid;
-       unsigned mode;
+       unsigned short mode;
        /* guilty gets set when shipping any suspects to the final
         * blame list instead of other commits
         */
@@ -177,6 +177,6 @@ struct blame_entry *blame_entry_prepend(struct blame_entry *head,
                                        long start, long end,
                                        struct blame_origin *o);
 
-extern struct blame_origin *get_blame_suspects(struct commit *commit);
+struct blame_origin *get_blame_suspects(struct commit *commit);
 
 #endif /* BLAME_H */
index 28b81a7e0256b2923c0fe296d9618d130821cdc4..643694542a51634e0bc5cbef92226913d16ccbe7 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -5,6 +5,7 @@
 #include "refs.h"
 #include "refspec.h"
 #include "remote.h"
+#include "sequencer.h"
 #include "commit.h"
 #include "worktree.h"
 
@@ -339,8 +340,7 @@ void create_branch(struct repository *r,
 
 void remove_branch_state(struct repository *r)
 {
-       unlink(git_path_cherry_pick_head(r));
-       unlink(git_path_revert_head(r));
+       sequencer_post_commit_cleanup(r);
        unlink(git_path_merge_head(r));
        unlink(git_path_merge_rr(r));
        unlink(git_path_merge_msg(r));
index 29c1afa4d0ce5d1639b81d3d652d5b16085218e9..6f38db14e9c496c55e204791e5ab0a5243186118 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -50,7 +50,7 @@ void create_branch(struct repository *r,
  * Return 1 if the named branch already exists; return 0 otherwise.
  * Fill ref with the full refname for the branch.
  */
-extern int validate_branchname(const char *name, struct strbuf *ref);
+int validate_branchname(const char *name, struct strbuf *ref);
 
 /*
  * Check if a branch 'name' can be created as a new branch; die otherwise.
@@ -58,7 +58,7 @@ extern int validate_branchname(const char *name, struct strbuf *ref);
  * Return 1 if the named branch already exists; return 0 otherwise.
  * Fill ref with the full refname for the branch.
  */
-extern int validate_new_branchname(const char *name, struct strbuf *ref, int force);
+int validate_new_branchname(const char *name, struct strbuf *ref, int force);
 
 /*
  * Remove information about the state of working on the current
@@ -72,26 +72,26 @@ void remove_branch_state(struct repository *r);
  * Returns 0 on success.
  */
 #define BRANCH_CONFIG_VERBOSE 01
-extern int install_branch_config(int flag, const char *local, const char *origin, const char *remote);
+int install_branch_config(int flag, const char *local, const char *origin, const char *remote);
 
 /*
  * Read branch description
  */
-extern int read_branch_desc(struct strbuf *, const char *branch_name);
+int read_branch_desc(struct strbuf *, const char *branch_name);
 
 /*
  * Check if a branch is checked out in the main worktree or any linked
  * worktree and die (with a message describing its checkout location) if
  * it is.
  */
-extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
+void die_if_checked_out(const char *branch, int ignore_current_worktree);
 
 /*
  * Update all per-worktree HEADs pointing at the old ref to point the new ref.
  * This will be used when renaming a branch. Returns 0 if successful, non-zero
  * otherwise.
  */
-extern int replace_each_worktree_head_symref(const char *oldref, const char *newref,
-                                            const char *logmsg);
+int replace_each_worktree_head_symref(const char *oldref, const char *newref,
+                                     const char *logmsg);
 
 #endif
index b78ab6e30b26f6a079c2f747c3787ead04b57962..ec7e0954c4c8a1da896392bd28158abb74667898 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -102,7 +102,7 @@ extern const char git_more_info_string[];
 #define PRUNE_PACKED_DRY_RUN 01
 #define PRUNE_PACKED_VERBOSE 02
 
-extern void prune_packed_objects(int);
+void prune_packed_objects(int);
 
 struct fmt_merge_msg_opts {
        unsigned add_title:1,
@@ -110,8 +110,8 @@ struct fmt_merge_msg_opts {
        int shortlog_len;
 };
 
-extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
-                        struct fmt_merge_msg_opts *);
+int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
+                 struct fmt_merge_msg_opts *);
 
 /**
  * If a built-in has DELAY_PAGER_CONFIG set, the built-in should call this early
@@ -123,132 +123,131 @@ extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
  * You should most likely use a default of 0 or 1. "Punt" (-1) could be useful
  * to be able to fall back to some historical compatibility name.
  */
-extern void setup_auto_pager(const char *cmd, int def);
+void setup_auto_pager(const char *cmd, int def);
 
-extern int is_builtin(const char *s);
+int is_builtin(const char *s);
 
-extern int cmd_add(int argc, const char **argv, const char *prefix);
-extern int cmd_am(int argc, const char **argv, const char *prefix);
-extern int cmd_annotate(int argc, const char **argv, const char *prefix);
-extern int cmd_apply(int argc, const char **argv, const char *prefix);
-extern int cmd_archive(int argc, const char **argv, const char *prefix);
-extern int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
-extern int cmd_blame(int argc, const char **argv, const char *prefix);
-extern int cmd_branch(int argc, const char **argv, const char *prefix);
-extern int cmd_bundle(int argc, const char **argv, const char *prefix);
-extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
-extern int cmd_checkout(int argc, const char **argv, const char *prefix);
-extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
-extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
-extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);
-extern int cmd_check_mailmap(int argc, const char **argv, const char *prefix);
-extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
-extern int cmd_cherry(int argc, const char **argv, const char *prefix);
-extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
-extern int cmd_clone(int argc, const char **argv, const char *prefix);
-extern int cmd_clean(int argc, const char **argv, const char *prefix);
-extern int cmd_column(int argc, const char **argv, const char *prefix);
-extern int cmd_commit(int argc, const char **argv, const char *prefix);
-extern int cmd_commit_graph(int argc, const char **argv, const char *prefix);
-extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_config(int argc, const char **argv, const char *prefix);
-extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
-extern int cmd_credential(int argc, const char **argv, const char *prefix);
-extern int cmd_describe(int argc, const char **argv, const char *prefix);
-extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
-extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
-extern int cmd_diff(int argc, const char **argv, const char *prefix);
-extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_difftool(int argc, const char **argv, const char *prefix);
-extern int cmd_fast_export(int argc, const char **argv, const char *prefix);
-extern int cmd_fetch(int argc, const char **argv, const char *prefix);
-extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
-extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
-extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
-extern int cmd_fsck(int argc, const char **argv, const char *prefix);
-extern int cmd_gc(int argc, const char **argv, const char *prefix);
-extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
-extern int cmd_grep(int argc, const char **argv, const char *prefix);
-extern int cmd_hash_object(int argc, const char **argv, const char *prefix);
-extern int cmd_help(int argc, const char **argv, const char *prefix);
-extern int cmd_index_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_init_db(int argc, const char **argv, const char *prefix);
-extern int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);
-extern int cmd_log(int argc, const char **argv, const char *prefix);
-extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
-extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
-extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
-extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
-extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
-extern int cmd_merge(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_index(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_mktag(int argc, const char **argv, const char *prefix);
-extern int cmd_mktree(int argc, const char **argv, const char *prefix);
-extern int cmd_multi_pack_index(int argc, const char **argv, const char *prefix);
-extern int cmd_mv(int argc, const char **argv, const char *prefix);
-extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
-extern int cmd_notes(int argc, const char **argv, const char *prefix);
-extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
-extern int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
-extern int cmd_patch_id(int argc, const char **argv, const char *prefix);
-extern int cmd_prune(int argc, const char **argv, const char *prefix);
-extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
-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(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);
-extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);
-extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
-extern int cmd_repack(int argc, const char **argv, const char *prefix);
-extern int cmd_rerere(int argc, const char **argv, const char *prefix);
-extern int cmd_reset(int argc, const char **argv, const char *prefix);
-extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
-extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
-extern int cmd_revert(int argc, const char **argv, const char *prefix);
-extern int cmd_rm(int argc, const char **argv, const char *prefix);
-extern int cmd_send_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_serve(int argc, const char **argv, const char *prefix);
-extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
-extern int cmd_show(int argc, const char **argv, const char *prefix);
-extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
-extern int cmd_show_index(int argc, const char **argv, const char *prefix);
-extern int cmd_status(int argc, const char **argv, const char *prefix);
-extern int cmd_stash(int argc, const char **argv, const char *prefix);
-extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
-extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
-extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
-extern int cmd_tag(int argc, const char **argv, const char *prefix);
-extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_unpack_file(int argc, const char **argv, const char *prefix);
-extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
-extern int cmd_update_index(int argc, const char **argv, const char *prefix);
-extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
-extern int cmd_update_server_info(int argc, const char **argv, const char *prefix);
-extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
-extern int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
-extern int cmd_upload_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_var(int argc, const char **argv, const char *prefix);
-extern int cmd_verify_commit(int argc, const char **argv, const char *prefix);
-extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
-extern int cmd_version(int argc, const char **argv, const char *prefix);
-extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
-extern int cmd_worktree(int argc, const char **argv, const char *prefix);
-extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
-extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
-extern int cmd_replace(int argc, const char **argv, const char *prefix);
+int cmd_add(int argc, const char **argv, const char *prefix);
+int cmd_am(int argc, const char **argv, const char *prefix);
+int cmd_annotate(int argc, const char **argv, const char *prefix);
+int cmd_apply(int argc, const char **argv, const char *prefix);
+int cmd_archive(int argc, const char **argv, const char *prefix);
+int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
+int cmd_blame(int argc, const char **argv, const char *prefix);
+int cmd_branch(int argc, const char **argv, const char *prefix);
+int cmd_bundle(int argc, const char **argv, const char *prefix);
+int cmd_cat_file(int argc, const char **argv, const char *prefix);
+int cmd_checkout(int argc, const char **argv, const char *prefix);
+int cmd_checkout_index(int argc, const char **argv, const char *prefix);
+int cmd_check_attr(int argc, const char **argv, const char *prefix);
+int cmd_check_ignore(int argc, const char **argv, const char *prefix);
+int cmd_check_mailmap(int argc, const char **argv, const char *prefix);
+int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
+int cmd_cherry(int argc, const char **argv, const char *prefix);
+int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
+int cmd_clone(int argc, const char **argv, const char *prefix);
+int cmd_clean(int argc, const char **argv, const char *prefix);
+int cmd_column(int argc, const char **argv, const char *prefix);
+int cmd_commit(int argc, const char **argv, const char *prefix);
+int cmd_commit_graph(int argc, const char **argv, const char *prefix);
+int cmd_commit_tree(int argc, const char **argv, const char *prefix);
+int cmd_config(int argc, const char **argv, const char *prefix);
+int cmd_count_objects(int argc, const char **argv, const char *prefix);
+int cmd_credential(int argc, const char **argv, const char *prefix);
+int cmd_describe(int argc, const char **argv, const char *prefix);
+int cmd_diff_files(int argc, const char **argv, const char *prefix);
+int cmd_diff_index(int argc, const char **argv, const char *prefix);
+int cmd_diff(int argc, const char **argv, const char *prefix);
+int cmd_diff_tree(int argc, const char **argv, const char *prefix);
+int cmd_difftool(int argc, const char **argv, const char *prefix);
+int cmd_fast_export(int argc, const char **argv, const char *prefix);
+int cmd_fetch(int argc, const char **argv, const char *prefix);
+int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
+int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
+int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
+int cmd_format_patch(int argc, const char **argv, const char *prefix);
+int cmd_fsck(int argc, const char **argv, const char *prefix);
+int cmd_gc(int argc, const char **argv, const char *prefix);
+int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
+int cmd_grep(int argc, const char **argv, const char *prefix);
+int cmd_hash_object(int argc, const char **argv, const char *prefix);
+int cmd_help(int argc, const char **argv, const char *prefix);
+int cmd_index_pack(int argc, const char **argv, const char *prefix);
+int cmd_init_db(int argc, const char **argv, const char *prefix);
+int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);
+int cmd_log(int argc, const char **argv, const char *prefix);
+int cmd_log_reflog(int argc, const char **argv, const char *prefix);
+int cmd_ls_files(int argc, const char **argv, const char *prefix);
+int cmd_ls_tree(int argc, const char **argv, const char *prefix);
+int cmd_ls_remote(int argc, const char **argv, const char *prefix);
+int cmd_mailinfo(int argc, const char **argv, const char *prefix);
+int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+int cmd_merge(int argc, const char **argv, const char *prefix);
+int cmd_merge_base(int argc, const char **argv, const char *prefix);
+int cmd_merge_index(int argc, const char **argv, const char *prefix);
+int cmd_merge_ours(int argc, const char **argv, const char *prefix);
+int cmd_merge_file(int argc, const char **argv, const char *prefix);
+int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
+int cmd_merge_tree(int argc, const char **argv, const char *prefix);
+int cmd_mktag(int argc, const char **argv, const char *prefix);
+int cmd_mktree(int argc, const char **argv, const char *prefix);
+int cmd_multi_pack_index(int argc, const char **argv, const char *prefix);
+int cmd_mv(int argc, const char **argv, const char *prefix);
+int cmd_name_rev(int argc, const char **argv, const char *prefix);
+int cmd_notes(int argc, const char **argv, const char *prefix);
+int cmd_pack_objects(int argc, const char **argv, const char *prefix);
+int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
+int cmd_patch_id(int argc, const char **argv, const char *prefix);
+int cmd_prune(int argc, const char **argv, const char *prefix);
+int cmd_prune_packed(int argc, const char **argv, const char *prefix);
+int cmd_pull(int argc, const char **argv, const char *prefix);
+int cmd_push(int argc, const char **argv, const char *prefix);
+int cmd_range_diff(int argc, const char **argv, const char *prefix);
+int cmd_read_tree(int argc, const char **argv, const char *prefix);
+int cmd_rebase(int argc, const char **argv, const char *prefix);
+int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
+int cmd_receive_pack(int argc, const char **argv, const char *prefix);
+int cmd_reflog(int argc, const char **argv, const char *prefix);
+int cmd_remote(int argc, const char **argv, const char *prefix);
+int cmd_remote_ext(int argc, const char **argv, const char *prefix);
+int cmd_remote_fd(int argc, const char **argv, const char *prefix);
+int cmd_repack(int argc, const char **argv, const char *prefix);
+int cmd_rerere(int argc, const char **argv, const char *prefix);
+int cmd_reset(int argc, const char **argv, const char *prefix);
+int cmd_rev_list(int argc, const char **argv, const char *prefix);
+int cmd_rev_parse(int argc, const char **argv, const char *prefix);
+int cmd_revert(int argc, const char **argv, const char *prefix);
+int cmd_rm(int argc, const char **argv, const char *prefix);
+int cmd_send_pack(int argc, const char **argv, const char *prefix);
+int cmd_shortlog(int argc, const char **argv, const char *prefix);
+int cmd_show(int argc, const char **argv, const char *prefix);
+int cmd_show_branch(int argc, const char **argv, const char *prefix);
+int cmd_show_index(int argc, const char **argv, const char *prefix);
+int cmd_status(int argc, const char **argv, const char *prefix);
+int cmd_stash(int argc, const char **argv, const char *prefix);
+int cmd_stripspace(int argc, const char **argv, const char *prefix);
+int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
+int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
+int cmd_tag(int argc, const char **argv, const char *prefix);
+int cmd_tar_tree(int argc, const char **argv, const char *prefix);
+int cmd_unpack_file(int argc, const char **argv, const char *prefix);
+int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
+int cmd_update_index(int argc, const char **argv, const char *prefix);
+int cmd_update_ref(int argc, const char **argv, const char *prefix);
+int cmd_update_server_info(int argc, const char **argv, const char *prefix);
+int cmd_upload_archive(int argc, const char **argv, const char *prefix);
+int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
+int cmd_upload_pack(int argc, const char **argv, const char *prefix);
+int cmd_var(int argc, const char **argv, const char *prefix);
+int cmd_verify_commit(int argc, const char **argv, const char *prefix);
+int cmd_verify_tag(int argc, const char **argv, const char *prefix);
+int cmd_version(int argc, const char **argv, const char *prefix);
+int cmd_whatchanged(int argc, const char **argv, const char *prefix);
+int cmd_worktree(int argc, const char **argv, const char *prefix);
+int cmd_write_tree(int argc, const char **argv, const char *prefix);
+int cmd_verify_pack(int argc, const char **argv, const char *prefix);
+int cmd_show_ref(int argc, const char **argv, const char *prefix);
+int cmd_pack_refs(int argc, const char **argv, const char *prefix);
+int cmd_replace(int argc, const char **argv, const char *prefix);
 
 #endif
index db2dfa43502d0059219189f01731e143982345ab..dd18e5c9b67038307401e8d33970d5c424c03cb7 100644 (file)
@@ -374,11 +374,12 @@ static int add_files(struct dir_struct *dir, int flags)
        }
 
        for (i = 0; i < dir->nr; i++) {
-               check_embedded_repo(dir->entries[i]->name);
                if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
                        if (!ignore_add_errors)
                                die(_("adding files failed"));
                        exit_status = 1;
+               } else {
+                       check_embedded_repo(dir->entries[i]->name);
                }
        }
        return exit_status;
index 50bde9961809b1d55c74fb6d3cbfbf5a86ddf5c7..ffdd94e8f66193626e343e4cd0b14bd12d262d0e 100644 (file)
@@ -66,6 +66,7 @@ static int option_dissociate;
 static int max_jobs = -1;
 static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
 static struct list_objects_filter_options filter_options;
+static struct string_list server_options = STRING_LIST_INIT_NODUP;
 
 static int recurse_submodules_cb(const struct option *opt,
                                 const char *arg, int unset)
@@ -137,6 +138,8 @@ static struct option builtin_clone_options[] = {
                   N_("separate git dir from working tree")),
        OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
                        N_("set config inside the new repository")),
+       OPT_STRING_LIST(0, "server-option", &server_options,
+                       N_("server-specific"), N_("option to transmit")),
        OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
                        TRANSPORT_FAMILY_IPV4),
        OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
@@ -657,7 +660,8 @@ static void update_remote_refs(const struct ref *refs,
                               const char *branch_top,
                               const char *msg,
                               struct transport *transport,
-                              int check_connectivity)
+                              int check_connectivity,
+                              int check_refs_only)
 {
        const struct ref *rm = mapped_refs;
 
@@ -666,6 +670,7 @@ static void update_remote_refs(const struct ref *refs,
 
                opt.transport = transport;
                opt.progress = transport->progress;
+               opt.check_refs_only = !!check_refs_only;
 
                if (check_connected(iterate_ref_map, &rm, &opt))
                        die(_("remote did not send all necessary objects"));
@@ -1136,6 +1141,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                transport_set_option(transport, TRANS_OPT_UPLOADPACK,
                                     option_upload_pack);
 
+       if (server_options.nr)
+               transport->server_options = &server_options;
+
        if (filter_options.choice) {
                struct strbuf expanded_filter_spec = STRBUF_INIT;
                expand_list_objects_filter_spec(&filter_options,
@@ -1224,7 +1232,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
                           branch_top.buf, reflog_msg.buf, transport,
-                          !is_local);
+                          !is_local, filter_options.choice);
 
        update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
index 833ecb316a8fa5a8bef7bc5894c2f6d571c57427..1c9e8e2228c7ce58375bc247c4ad5850a1bd7d2d 100644 (file)
@@ -668,6 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        const char *hook_arg2 = NULL;
        int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
        int old_display_comment_prefix;
+       int merge_contains_scissors = 0;
 
        /* This checks and barfs if author is badly specified */
        determine_author_info(author_ident);
@@ -728,6 +729,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                        strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
        } else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
+               size_t merge_msg_start;
+
                /*
                 * prepend SQUASH_MSG here if it exists and a
                 * "merge --squash" was originally performed
@@ -738,8 +741,16 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                        hook_arg1 = "squash";
                } else
                        hook_arg1 = "merge";
+
+               merge_msg_start = sb.len;
                if (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)
                        die_errno(_("could not read MERGE_MSG"));
+
+               if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+                   wt_status_locate_end(sb.buf + merge_msg_start,
+                                        sb.len - merge_msg_start) <
+                               sb.len - merge_msg_start)
+                       merge_contains_scissors = 1;
        } else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
                if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
                        die_errno(_("could not read SQUASH_MSG"));
@@ -807,7 +818,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                struct ident_split ci, ai;
 
                if (whence != FROM_COMMIT) {
-                       if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+                       if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+                               !merge_contains_scissors)
                                wt_status_add_cut_line(s->fp);
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                            whence == FROM_MERGE
@@ -832,10 +844,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                _("Please enter the commit message for your changes."
                                  " Lines starting\nwith '%c' will be ignored, and an empty"
                                  " message aborts the commit.\n"), comment_line_char);
-               else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
-                        whence == FROM_COMMIT)
-                       wt_status_add_cut_line(s->fp);
-               else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
+               else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+                       if (whence == FROM_COMMIT && !merge_contains_scissors)
+                               wt_status_add_cut_line(s->fp);
+               else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
                        status_printf(s, GIT_COLOR_NORMAL,
                                _("Please enter the commit message for your changes."
                                  " Lines starting\n"
@@ -1172,24 +1184,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
                die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
        if (argc == 0 && (also || (only && !amend && !allow_empty)))
                die(_("No paths with --include/--only does not make sense."));
-       if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
-               cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
-                                           COMMIT_MSG_CLEANUP_SPACE;
-       else if (!strcmp(cleanup_arg, "verbatim"))
-               cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
-       else if (!strcmp(cleanup_arg, "whitespace"))
-               cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
-       else if (!strcmp(cleanup_arg, "strip"))
-               cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
-       else if (!strcmp(cleanup_arg, "scissors"))
-               cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
-                                           COMMIT_MSG_CLEANUP_SPACE;
-       /*
-        * Please update _git_commit() in git-completion.bash when you
-        * add new options.
-        */
-       else
-               die(_("Invalid cleanup mode %s"), cleanup_arg);
+       cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
 
        handle_untracked_files_arg(s);
 
@@ -1491,7 +1486,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
                OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
                OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
-               OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
+               OPT_CLEANUP(&cleanup_arg),
                OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
                { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
                  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
@@ -1627,11 +1622,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                die(_("could not read commit message: %s"), strerror(saved_errno));
        }
 
-       if (verbose || /* Truncate the message just before the diff, if any. */
-           cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
-               strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
-       if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
-               strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+       cleanup_message(&sb, cleanup_mode, verbose);
 
        if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
                rollback_index_files();
@@ -1667,8 +1658,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                die("%s", err.buf);
        }
 
-       unlink(git_path_cherry_pick_head(the_repository));
-       unlink(git_path_revert_head(the_repository));
+       sequencer_post_commit_cleanup(the_repository);
        unlink(git_path_merge_head(the_repository));
        unlink(git_path_merge_msg(the_repository));
        unlink(git_path_merge_mode(the_repository));
index 5ce8946d390c1c42b885a2c86a363f79aec1b27a..e96f72af8044769df6b4ef42f0525b572b0c39e2 100644 (file)
@@ -38,6 +38,7 @@
 #include "tag.h"
 #include "alias.h"
 #include "commit-reach.h"
+#include "wt-status.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -98,6 +99,9 @@ enum ff_type {
 
 static enum ff_type fast_forward = FF_ALLOW;
 
+static const char *cleanup_arg;
+static enum commit_msg_cleanup_mode cleanup_mode;
+
 static int option_parse_message(const struct option *opt,
                                const char *arg, int unset)
 {
@@ -249,6 +253,7 @@ static struct option builtin_merge_options[] = {
                N_("perform a commit if the merge succeeds (default)")),
        OPT_BOOL('e', "edit", &option_edit,
                N_("edit message before committing")),
+       OPT_CLEANUP(&cleanup_arg),
        OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
        OPT_SET_INT_F(0, "ff-only", &fast_forward,
                      N_("abort if fast-forward is not possible"),
@@ -612,6 +617,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
                return git_config_string(&pull_twohead, k, v);
        else if (!strcmp(k, "pull.octopus"))
                return git_config_string(&pull_octopus, k, v);
+       else if (!strcmp(k, "commit.cleanup"))
+               return git_config_string(&cleanup_arg, k, v);
        else if (!strcmp(k, "merge.renormalize"))
                option_renormalize = git_config_bool(k, v);
        else if (!strcmp(k, "merge.ff")) {
@@ -800,8 +807,13 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)
 static const char merge_editor_comment[] =
 N_("Please enter a commit message to explain why this merge is necessary,\n"
    "especially if it merges an updated upstream into a topic branch.\n"
-   "\n"
-   "Lines starting with '%c' will be ignored, and an empty message aborts\n"
+   "\n");
+
+static const char scissors_editor_comment[] =
+N_("An empty message aborts the commit.\n");
+
+static const char no_scissors_editor_comment[] =
+N_("Lines starting with '%c' will be ignored, and an empty message aborts\n"
    "the commit.\n");
 
 static void write_merge_heads(struct commit_list *);
@@ -809,11 +821,19 @@ static void prepare_to_commit(struct commit_list *remoteheads)
 {
        struct strbuf msg = STRBUF_INIT;
        strbuf_addbuf(&msg, &merge_msg);
-       strbuf_addch(&msg, '\n');
        if (squash)
                BUG("the control must not reach here under --squash");
-       if (0 < option_edit)
-               strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
+       if (0 < option_edit) {
+               strbuf_addch(&msg, '\n');
+               if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+                       wt_status_append_cut_line(&msg);
+                       strbuf_commented_addf(&msg, "\n");
+               }
+               strbuf_commented_addf(&msg, _(merge_editor_comment));
+               strbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?
+                       scissors_editor_comment :
+                       no_scissors_editor_comment), comment_line_char);
+       }
        if (signoff)
                append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
        write_merge_heads(remoteheads);
@@ -832,7 +852,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                abort_commit(remoteheads, NULL);
 
        read_merge_msg(&msg);
-       strbuf_stripspace(&msg, 0 < option_edit);
+       cleanup_message(&msg, cleanup_mode, 0);
        if (!msg.len)
                abort_commit(remoteheads, _("Empty commit message."));
        strbuf_release(&merge_msg);
@@ -880,7 +900,6 @@ static int finish_automerge(struct commit *head,
        parents = remoteheads;
        if (!head_subsumed || fast_forward == FF_NO)
                commit_list_insert(head, &parents);
-       strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
        if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
                        &result_commit, NULL, sign_commit))
@@ -901,7 +920,15 @@ static int suggest_conflicts(void)
        filename = git_path_merge_msg(the_repository);
        fp = xfopen(filename, "a");
 
-       append_conflicts_hint(&the_index, &msgbuf);
+       /*
+        * We can't use cleanup_mode because if we're not using the editor,
+        * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even
+        * though the message is meant to be processed later by git-commit.
+        * Thus, we will get the cleanup mode which is returned when we _are_
+        * using an editor.
+        */
+       append_conflicts_hint(&the_index, &msgbuf,
+                             get_cleanup_mode(cleanup_arg, 1));
        fputs(msgbuf.buf, fp);
        strbuf_release(&msgbuf);
        fclose(fp);
@@ -1301,6 +1328,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        }
        resolve_undo_clear();
 
+       if (option_edit < 0)
+               option_edit = default_edit_option();
+
+       cleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit);
+
        if (verbosity < 0)
                show_diffstat = 0;
 
@@ -1386,9 +1418,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        fast_forward = FF_NO;
        }
 
-       if (option_edit < 0)
-               option_edit = default_edit_option();
-
        if (!use_strategies) {
                if (!remoteheads)
                        ; /* already up-to-date */
index 2d9a3bdc9d83e24759b5a8892c26863bd9829c34..9f424aababe6beacec572d6ef7dcd3b5fa18de44 100644 (file)
@@ -97,7 +97,7 @@ static off_t reuse_packfile_offset;
 static int use_bitmap_index_default = 1;
 static int use_bitmap_index = -1;
 static int write_bitmap_index;
-static uint16_t write_bitmap_options;
+static uint16_t write_bitmap_options = BITMAP_OPT_HASH_CACHE;
 
 static int exclude_promisor_objects;
 
@@ -964,6 +964,8 @@ static void write_pack_file(void)
        if (written != nr_result)
                die(_("wrote %"PRIu32" objects while expecting %"PRIu32),
                    written, nr_result);
+       trace2_data_intmax("pack-objects", the_repository,
+                          "write_pack_file/wrote", nr_result);
 }
 
 static int no_try_delta(const char *path)
index 9bd6a78081c2b2ff7b53cb98d8f1183bd9275ad8..9dd32a115bbbfeedb1c1648348ddd32ccaa9b641 100644 (file)
@@ -24,6 +24,7 @@
 #include "lockfile.h"
 #include "wt-status.h"
 #include "commit-reach.h"
+#include "sequencer.h"
 
 enum rebase_type {
        REBASE_INVALID = -1,
@@ -101,6 +102,7 @@ static char *opt_signoff;
 static char *opt_squash;
 static char *opt_commit;
 static char *opt_edit;
+static char *cleanup_arg;
 static char *opt_ff;
 static char *opt_verify_signatures;
 static int opt_autostash = -1;
@@ -168,6 +170,7 @@ static struct option pull_options[] = {
        OPT_PASSTHRU(0, "edit", &opt_edit, NULL,
                N_("edit message before committing"),
                PARSE_OPT_NOARG),
+       OPT_CLEANUP(&cleanup_arg),
        OPT_PASSTHRU(0, "ff", &opt_ff, NULL,
                N_("allow fast-forward"),
                PARSE_OPT_NOARG),
@@ -645,6 +648,8 @@ static int run_merge(void)
                argv_array_push(&args, opt_commit);
        if (opt_edit)
                argv_array_push(&args, opt_edit);
+       if (cleanup_arg)
+               argv_array_pushf(&args, "--cleanup=%s", cleanup_arg);
        if (opt_ff)
                argv_array_push(&args, opt_ff);
        if (opt_verify_signatures)
@@ -876,6 +881,13 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
 
+       if (cleanup_arg)
+               /*
+                * this only checks the validity of cleanup_arg; we don't need
+                * a valid value for use_editor
+                */
+               get_cleanup_mode(cleanup_arg, 0);
+
        parse_repo_refspecs(argc, argv, &repo, &refspecs);
 
        if (!opt_ff)
diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
deleted file mode 100644 (file)
index 4535523..0000000
+++ /dev/null
@@ -1,377 +0,0 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#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 add_exec_commands(struct string_list *commands)
-{
-       const char *todo_file = rebase_path_todo();
-       struct todo_list todo_list = TODO_LIST_INIT;
-       int res;
-
-       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
-               return error_errno(_("could not read '%s'."), todo_file);
-
-       if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
-                                       &todo_list)) {
-               todo_list_release(&todo_list);
-               return error(_("unusable todo list: '%s'"), todo_file);
-       }
-
-       todo_list_add_exec_commands(&todo_list, commands);
-       res = todo_list_write_to_file(the_repository, &todo_list,
-                                     todo_file, NULL, NULL, -1, 0);
-       todo_list_release(&todo_list);
-
-       if (res)
-               return error_errno(_("could not write '%s'."), todo_file);
-       return 0;
-}
-
-static int rearrange_squash_in_todo_file(void)
-{
-       const char *todo_file = rebase_path_todo();
-       struct todo_list todo_list = TODO_LIST_INIT;
-       int res = 0;
-
-       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
-               return error_errno(_("could not read '%s'."), todo_file);
-       if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
-                                       &todo_list)) {
-               todo_list_release(&todo_list);
-               return error(_("unusable todo list: '%s'"), todo_file);
-       }
-
-       res = todo_list_rearrange_squash(&todo_list);
-       if (!res)
-               res = todo_list_write_to_file(the_repository, &todo_list,
-                                             todo_file, NULL, NULL, -1, 0);
-
-       todo_list_release(&todo_list);
-
-       if (res)
-               return error_errno(_("could not write '%s'."), todo_file);
-       return 0;
-}
-
-static int transform_todo_file(unsigned flags)
-{
-       const char *todo_file = rebase_path_todo();
-       struct todo_list todo_list = TODO_LIST_INIT;
-       int res;
-
-       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
-               return error_errno(_("could not read '%s'."), todo_file);
-
-       if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
-                                       &todo_list)) {
-               todo_list_release(&todo_list);
-               return error(_("unusable todo list: '%s'"), todo_file);
-       }
-
-       res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
-                                     NULL, NULL, -1, flags);
-       todo_list_release(&todo_list);
-
-       if (res)
-               return error_errno(_("could not write '%s'."), todo_file);
-       return 0;
-}
-
-static int edit_todo_file(unsigned flags)
-{
-       const char *todo_file = rebase_path_todo();
-       struct todo_list todo_list = TODO_LIST_INIT,
-               new_todo = TODO_LIST_INIT;
-       int res = 0;
-
-       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
-               return error_errno(_("could not read '%s'."), todo_file);
-
-       strbuf_stripspace(&todo_list.buf, 1);
-       res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
-       if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
-                                           NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
-               res = error_errno(_("could not write '%s'"), todo_file);
-
-       todo_list_release(&todo_list);
-       todo_list_release(&new_todo);
-
-       return res;
-}
-
-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,
-                                struct string_list *commands, unsigned autosquash)
-{
-       int ret;
-       const char *head_hash = NULL;
-       char *revisions = NULL, *shortrevisions = NULL;
-       struct argv_array make_script_args = ARGV_ARRAY_INIT;
-       struct todo_list todo_list = TODO_LIST_INIT;
-
-       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);
-
-       argv_array_pushl(&make_script_args, "", revisions, NULL);
-       if (restrict_revision)
-               argv_array_push(&make_script_args, restrict_revision);
-
-       ret = sequencer_make_script(the_repository, &todo_list.buf,
-                                   make_script_args.argc, make_script_args.argv,
-                                   flags);
-
-       if (ret)
-               error(_("could not generate todo list"));
-       else {
-               discard_cache();
-               if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
-                                               &todo_list))
-                       BUG("unusable todo list");
-
-               ret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,
-                                     onto, head_hash, commands, autosquash, &todo_list);
-       }
-
-       free(revisions);
-       free(shortrevisions);
-       todo_list_release(&todo_list);
-       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;
-       struct string_list commands = STRING_LIST_INIT_DUP;
-       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_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
-                        N_("automatically re-schedule any `exec` that fails")),
-               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"));
-
-       if (cmd && *cmd) {
-               string_list_split(&commands, cmd, '\n', -1);
-
-               /* rebase.c adds a new line to cmd after every command,
-                * so here the last command is always empty */
-               string_list_remove_empty_items(&commands, 0);
-       }
-
-       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, &commands, autosquash);
-               break;
-       case SKIP: {
-               struct string_list merge_rr = STRING_LIST_INIT_DUP;
-
-               rerere_clear(the_repository, &merge_rr);
-               /* fallthrough */
-       case CONTINUE:
-               ret = sequencer_continue(the_repository, &opts);
-               break;
-       }
-       case EDIT_TODO:
-               ret = edit_todo_file(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_todo_file(flags);
-               break;
-       case CHECK_TODO_LIST:
-               ret = check_todo_list_from_file(the_repository);
-               break;
-       case REARRANGE_SQUASH:
-               ret = rearrange_squash_in_todo_file();
-               break;
-       case ADD_EXEC:
-               ret = add_exec_commands(&commands);
-               break;
-       default:
-               BUG("invalid command '%d'", command);
-       }
-
-       string_list_clear(&commands, 0);
-       return !!ret;
-}
index 2e41ad5644c11db0d496aa3c8374cc194b331e78..ba3a574e4022c3dbd0e037782f520b1a199da42e 100644 (file)
@@ -25,6 +25,8 @@
 #include "commit-reach.h"
 #include "rerere.h"
 #include "branch.h"
+#include "sequencer.h"
+#include "rebase-interactive.h"
 
 static char const * const builtin_rebase_usage[] = {
        N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
@@ -35,6 +37,8 @@ static char const * const builtin_rebase_usage[] = {
        NULL
 };
 
+static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
+static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
 static GIT_PATH_FUNC(apply_dir, "rebase-apply")
 static GIT_PATH_FUNC(merge_dir, "rebase-merge")
 
@@ -86,6 +90,437 @@ struct rebase_options {
        int use_legacy_rebase;
 };
 
+#define REBASE_OPTIONS_INIT {                          \
+               .type = REBASE_UNSPECIFIED,             \
+               .flags = REBASE_NO_QUIET,               \
+               .git_am_opts = ARGV_ARRAY_INIT,         \
+               .git_format_patch_opt = STRBUF_INIT     \
+       }
+
+static struct replay_opts get_replay_opts(const struct rebase_options *opts)
+{
+       struct replay_opts replay = REPLAY_OPTS_INIT;
+
+       replay.action = REPLAY_INTERACTIVE_REBASE;
+       sequencer_init_config(&replay);
+
+       replay.signoff = opts->signoff;
+       replay.allow_ff = !(opts->flags & REBASE_FORCE);
+       if (opts->allow_rerere_autoupdate)
+               replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
+       replay.allow_empty = 1;
+       replay.allow_empty_message = opts->allow_empty_message;
+       replay.verbose = opts->flags & REBASE_VERBOSE;
+       replay.reschedule_failed_exec = opts->reschedule_failed_exec;
+       replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
+       replay.strategy = opts->strategy;
+       if (opts->strategy_opts)
+               parse_strategy_opts(&replay, opts->strategy_opts);
+
+       return replay;
+}
+
+enum action {
+       ACTION_NONE = 0,
+       ACTION_CONTINUE,
+       ACTION_SKIP,
+       ACTION_ABORT,
+       ACTION_QUIT,
+       ACTION_EDIT_TODO,
+       ACTION_SHOW_CURRENT_PATCH,
+       ACTION_SHORTEN_OIDS,
+       ACTION_EXPAND_OIDS,
+       ACTION_CHECK_TODO_LIST,
+       ACTION_REARRANGE_SQUASH,
+       ACTION_ADD_EXEC
+};
+
+static const char *action_names[] = { "undefined",
+                                     "continue",
+                                     "skip",
+                                     "abort",
+                                     "quit",
+                                     "edit_todo",
+                                     "show_current_patch" };
+
+static int add_exec_commands(struct string_list *commands)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       int res;
+
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+
+       if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+                                       &todo_list)) {
+               todo_list_release(&todo_list);
+               return error(_("unusable todo list: '%s'"), todo_file);
+       }
+
+       todo_list_add_exec_commands(&todo_list, commands);
+       res = todo_list_write_to_file(the_repository, &todo_list,
+                                     todo_file, NULL, NULL, -1, 0);
+       todo_list_release(&todo_list);
+
+       if (res)
+               return error_errno(_("could not write '%s'."), todo_file);
+       return 0;
+}
+
+static int rearrange_squash_in_todo_file(void)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       int res = 0;
+
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+       if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+                                       &todo_list)) {
+               todo_list_release(&todo_list);
+               return error(_("unusable todo list: '%s'"), todo_file);
+       }
+
+       res = todo_list_rearrange_squash(&todo_list);
+       if (!res)
+               res = todo_list_write_to_file(the_repository, &todo_list,
+                                             todo_file, NULL, NULL, -1, 0);
+
+       todo_list_release(&todo_list);
+
+       if (res)
+               return error_errno(_("could not write '%s'."), todo_file);
+       return 0;
+}
+
+static int transform_todo_file(unsigned flags)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       int res;
+
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+
+       if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+                                       &todo_list)) {
+               todo_list_release(&todo_list);
+               return error(_("unusable todo list: '%s'"), todo_file);
+       }
+
+       res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
+                                     NULL, NULL, -1, flags);
+       todo_list_release(&todo_list);
+
+       if (res)
+               return error_errno(_("could not write '%s'."), todo_file);
+       return 0;
+}
+
+static int edit_todo_file(unsigned flags)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT,
+               new_todo = TODO_LIST_INIT;
+       int res = 0;
+
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+
+       strbuf_stripspace(&todo_list.buf, 1);
+       res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
+       if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
+                                           NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
+               res = error_errno(_("could not write '%s'"), todo_file);
+
+       todo_list_release(&todo_list);
+       todo_list_release(&new_todo);
+
+       return res;
+}
+
+static int get_revision_ranges(struct commit *upstream, struct commit *onto,
+                              const char **head_hash,
+                              char **revisions, char **shortrevisions)
+{
+       struct commit *base_rev = upstream ? upstream : onto;
+       const char *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", oid_to_hex(&base_rev->object.oid),
+                                                  *head_hash);
+
+       shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
+
+       if (upstream) {
+               const char *shortrev;
+
+               shortrev = find_unique_abbrev(&base_rev->object.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,
+                           struct commit *onto, const char *orig_head)
+{
+       FILE *interactive;
+
+       if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
+               return error_errno(_("could not create temporary %s"), merge_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 void split_exec_commands(const char *cmd, struct string_list *commands)
+{
+       if (cmd && *cmd) {
+               string_list_split(commands, cmd, '\n', -1);
+
+               /* rebase.c adds a new line to cmd after every command,
+                * so here the last command is always empty */
+               string_list_remove_empty_items(commands, 0);
+       }
+}
+
+static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
+{
+       int ret;
+       const char *head_hash = NULL;
+       char *revisions = NULL, *shortrevisions = NULL;
+       struct argv_array make_script_args = ARGV_ARRAY_INIT;
+       struct todo_list todo_list = TODO_LIST_INIT;
+       struct replay_opts replay = get_replay_opts(opts);
+       struct string_list commands = STRING_LIST_INIT_DUP;
+
+       if (prepare_branch_to_be_rebased(the_repository, &replay,
+                                        opts->switch_to))
+               return -1;
+
+       if (get_revision_ranges(opts->upstream, opts->onto, &head_hash,
+                               &revisions, &shortrevisions))
+               return -1;
+
+       if (init_basic_state(&replay,
+                            opts->head_name ? opts->head_name : "detached HEAD",
+                            opts->onto, head_hash)) {
+               free(revisions);
+               free(shortrevisions);
+
+               return -1;
+       }
+
+       if (!opts->upstream && opts->squash_onto)
+               write_file(path_squash_onto(), "%s\n",
+                          oid_to_hex(opts->squash_onto));
+
+       argv_array_pushl(&make_script_args, "", revisions, NULL);
+       if (opts->restrict_revision)
+               argv_array_push(&make_script_args,
+                               oid_to_hex(&opts->restrict_revision->object.oid));
+
+       ret = sequencer_make_script(the_repository, &todo_list.buf,
+                                   make_script_args.argc, make_script_args.argv,
+                                   flags);
+
+       if (ret)
+               error(_("could not generate todo list"));
+       else {
+               discard_cache();
+               if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+                                               &todo_list))
+                       BUG("unusable todo list");
+
+               split_exec_commands(opts->cmd, &commands);
+               ret = complete_action(the_repository, &replay, flags,
+                       shortrevisions, opts->onto_name, opts->onto, head_hash,
+                       &commands, opts->autosquash, &todo_list);
+       }
+
+       string_list_clear(&commands, 0);
+       free(revisions);
+       free(shortrevisions);
+       todo_list_release(&todo_list);
+       argv_array_clear(&make_script_args);
+
+       return ret;
+}
+
+static int run_rebase_interactive(struct rebase_options *opts,
+                                 enum action command)
+{
+       unsigned flags = 0;
+       int abbreviate_commands = 0, ret = 0;
+
+       git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
+
+       flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
+       flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
+       flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
+       flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
+       flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
+
+       switch (command) {
+       case ACTION_NONE: {
+               if (!opts->onto && !opts->upstream)
+                       die(_("a base commit must be provided with --upstream or --onto"));
+
+               ret = do_interactive_rebase(opts, flags);
+               break;
+       }
+       case ACTION_SKIP: {
+               struct string_list merge_rr = STRING_LIST_INIT_DUP;
+
+               rerere_clear(the_repository, &merge_rr);
+       }
+               /* fallthrough */
+       case ACTION_CONTINUE: {
+               struct replay_opts replay_opts = get_replay_opts(opts);
+
+               ret = sequencer_continue(the_repository, &replay_opts);
+               break;
+       }
+       case ACTION_EDIT_TODO:
+               ret = edit_todo_file(flags);
+               break;
+       case ACTION_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 ACTION_SHORTEN_OIDS:
+       case ACTION_EXPAND_OIDS:
+               ret = transform_todo_file(flags);
+               break;
+       case ACTION_CHECK_TODO_LIST:
+               ret = check_todo_list_from_file(the_repository);
+               break;
+       case ACTION_REARRANGE_SQUASH:
+               ret = rearrange_squash_in_todo_file();
+               break;
+       case ACTION_ADD_EXEC: {
+               struct string_list commands = STRING_LIST_INIT_DUP;
+
+               split_exec_commands(opts->cmd, &commands);
+               ret = add_exec_commands(&commands);
+               string_list_clear(&commands, 0);
+               break;
+       }
+       default:
+               BUG("invalid command '%d'", command);
+       }
+
+       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 rebase_options opts = REBASE_OPTIONS_INIT;
+       struct object_id squash_onto = null_oid;
+       enum action command = ACTION_NONE;
+       struct option options[] = {
+               OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
+                          REBASE_FORCE),
+               OPT_BOOL(0, "keep-empty", &opts.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", &opts.rebase_merges, N_("rebase merge commits")),
+               OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
+                        N_("keep original branch points of cousins")),
+               OPT_BOOL(0, "autosquash", &opts.autosquash,
+                        N_("move commits that begin with squash!/fixup!")),
+               OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
+               OPT_BIT('v', "verbose", &opts.flags,
+                       N_("display a diffstat of what changed upstream"),
+                       REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
+               OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
+                           ACTION_CONTINUE),
+               OPT_CMDMODE(0, "skip", &command, N_("skip commit"), ACTION_SKIP),
+               OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
+                           ACTION_EDIT_TODO),
+               OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
+                           ACTION_SHOW_CURRENT_PATCH),
+               OPT_CMDMODE(0, "shorten-ids", &command,
+                       N_("shorten commit ids in the todo list"), ACTION_SHORTEN_OIDS),
+               OPT_CMDMODE(0, "expand-ids", &command,
+                       N_("expand commit ids in the todo list"), ACTION_EXPAND_OIDS),
+               OPT_CMDMODE(0, "check-todo-list", &command,
+                       N_("check the todo list"), ACTION_CHECK_TODO_LIST),
+               OPT_CMDMODE(0, "rearrange-squash", &command,
+                       N_("rearrange fixup/squash lines"), ACTION_REARRANGE_SQUASH),
+               OPT_CMDMODE(0, "add-exec-commands", &command,
+                       N_("insert exec commands in todo list"), ACTION_ADD_EXEC),
+               { OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"),
+                 PARSE_OPT_NONEG, parse_opt_commit, 0 },
+               { OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision,
+                 N_("restrict-revision"), N_("restrict revision"),
+                 PARSE_OPT_NONEG, parse_opt_commit, 0 },
+               { OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"),
+                 N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 },
+               { OPTION_CALLBACK, 0, "upstream", &opts.upstream, N_("upstream"),
+                 N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit,
+                 0 },
+               OPT_STRING(0, "head-name", &opts.head_name, N_("head-name"), N_("head name")),
+               { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, 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", &opts.strategy_opts, N_("strategy-opts"),
+                          N_("strategy options")),
+               OPT_STRING(0, "switch-to", &opts.switch_to, N_("switch-to"),
+                          N_("the branch or commit to checkout")),
+               OPT_STRING(0, "onto-name", &opts.onto_name, N_("onto-name"), N_("onto name")),
+               OPT_STRING(0, "cmd", &opts.cmd, N_("cmd"), N_("the command to run")),
+               OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_autoupdate),
+               OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
+                        N_("automatically re-schedule any `exec` that fails")),
+               OPT_END()
+       };
+
+       opts.rebase_cousins = -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);
+
+       if (!is_null_oid(&squash_onto))
+               opts.squash_onto = &squash_onto;
+
+       if (opts.rebase_cousins >= 0 && !opts.rebase_merges)
+               warning(_("--[no-]rebase-cousins has no effect without "
+                         "--rebase-merges"));
+
+       return !!run_rebase_interactive(&opts, command);
+}
+
 static int is_interactive(struct rebase_options *opts)
 {
        return opts->type == REBASE_INTERACTIVE ||
@@ -184,14 +619,13 @@ static int read_basic_state(struct rebase_options *opts)
                            &buf))
                        return -1;
                if (!strcmp(buf.buf, "--rerere-autoupdate"))
-                       opts->allow_rerere_autoupdate = 1;
+                       opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
                else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
-                       opts->allow_rerere_autoupdate = 0;
+                       opts->allow_rerere_autoupdate = RERERE_NOAUTOUPDATE;
                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);
@@ -223,7 +657,7 @@ static int read_basic_state(struct rebase_options *opts)
        return 0;
 }
 
-static int write_basic_state(struct rebase_options *opts)
+static int rebase_write_basic_state(struct rebase_options *opts)
 {
        write_file(state_dir_path("head-name", opts), "%s",
                   opts->head_name ? opts->head_name : "detached HEAD");
@@ -241,10 +675,11 @@ static int write_basic_state(struct rebase_options *opts)
        if (opts->strategy_opts)
                write_file(state_dir_path("strategy_opts", opts), "%s",
                           opts->strategy_opts);
-       if (opts->allow_rerere_autoupdate >= 0)
+       if (opts->allow_rerere_autoupdate > 0)
                write_file(state_dir_path("allow_rerere_autoupdate", opts),
                           "-%s-rerere-autoupdate",
-                          opts->allow_rerere_autoupdate ? "" : "-no");
+                          opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
+                               "" : "-no");
        if (opts->gpg_sign_opt)
                write_file(state_dir_path("gpg_sign_opt", opts), "%s",
                           opts->gpg_sign_opt);
@@ -608,9 +1043,9 @@ static int run_am(struct rebase_options *opts)
        argv_array_push(&am.args, "--rebasing");
        argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
        argv_array_push(&am.args, "--patch-format=mboxrd");
-       if (opts->allow_rerere_autoupdate > 0)
+       if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
                argv_array_push(&am.args, "--rerere-autoupdate");
-       else if (opts->allow_rerere_autoupdate == 0)
+       else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE)
                argv_array_push(&am.args, "--no-rerere-autoupdate");
        if (opts->gpg_sign_opt)
                argv_array_push(&am.args, opts->gpg_sign_opt);
@@ -623,12 +1058,12 @@ static int run_am(struct rebase_options *opts)
        }
 
        if (is_directory(opts->state_dir))
-               write_basic_state(opts);
+               rebase_write_basic_state(opts);
 
        return status;
 }
 
-static int run_specific_rebase(struct rebase_options *opts)
+static int run_specific_rebase(struct rebase_options *opts, enum action action)
 {
        const char *argv[] = { NULL, NULL };
        struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
@@ -637,77 +1072,19 @@ static int run_specific_rebase(struct rebase_options *opts)
 
        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);
+               setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
                if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
-                       argv_array_push(&child.env_array,
-                                       "GIT_SEQUENCE_EDITOR=:");
+                       setenv("GIT_SEQUENCE_EDITOR", ":", 1);
                        opts->autosquash = 0;
                }
+               if (opts->gpg_sign_opt) {
+                       /* remove the leading "-S" */
+                       char *tmp = xstrdup(opts->gpg_sign_opt + 2);
+                       free(opts->gpg_sign_opt);
+                       opts->gpg_sign_opt = tmp;
+               }
 
-               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");
-               if (opts->reschedule_failed_exec)
-                       argv_array_push(&child.args, "--reschedule-failed-exec");
-
-               status = run_command(&child);
+               status = run_rebase_interactive(opts, action);
                goto finished_rebase;
        }
 
@@ -747,9 +1124,9 @@ static int run_specific_rebase(struct rebase_options *opts)
        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");
+                       opts->allow_rerere_autoupdate == 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);
@@ -991,14 +1368,7 @@ static int check_exec_cmd(const char *cmd)
 
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
-       struct rebase_options options = {
-               .type = REBASE_UNSPECIFIED,
-               .flags = REBASE_NO_QUIET,
-               .git_am_opts = ARGV_ARRAY_INIT,
-               .allow_rerere_autoupdate  = -1,
-               .allow_empty_message = 1,
-               .git_format_patch_opt = STRBUF_INIT,
-       };
+       struct rebase_options options = REBASE_OPTIONS_INIT;
        const char *branch_name;
        int ret, flags, total_argc, in_progress = 0;
        int ok_to_skip_pre_rebase = 0;
@@ -1006,23 +1376,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        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;
-       static const char *action_names[] = { N_("undefined"),
-                                             N_("continue"),
-                                             N_("skip"),
-                                             N_("abort"),
-                                             N_("quit"),
-                                             N_("edit_todo"),
-                                             N_("show_current_patch"),
-                                             NULL };
+       enum action action = ACTION_NONE;
        const char *gpg_sign = NULL;
        struct string_list exec = STRING_LIST_INIT_NODUP;
        const char *rebase_merges = NULL;
@@ -1090,10 +1444,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                OPT_SET_INT('p', "preserve-merges", &options.type,
                            N_("(DEPRECATED) 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_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
                OPT_BOOL('k', "keep-empty", &options.keep_empty,
                         N_("preserve empty commits during rebase")),
                OPT_BOOL(0, "autosquash", &options.autosquash,
@@ -1139,6 +1490,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        trace_repo_setup(prefix);
        setup_work_tree();
 
+       options.allow_empty_message = 1;
        git_config(rebase_config, &options);
 
        if (options.use_legacy_rebase ||
@@ -1180,7 +1532,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                             builtin_rebase_options,
                             builtin_rebase_usage, 0);
 
-       if (action != NO_ACTION && total_argc != 2) {
+       if (action != ACTION_NONE && total_argc != 2) {
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
        }
@@ -1193,7 +1545,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                warning(_("git rebase --preserve-merges is deprecated. "
                          "Use --rebase-merges instead."));
 
-       if (action != NO_ACTION && !in_progress)
+       if (action != ACTION_NONE && !in_progress)
                die(_("No rebase in progress?"));
        setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
 
@@ -1293,7 +1645,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                options.action = "show-current-patch";
                options.dont_finish_rebase = 1;
                goto run_rebase;
-       case NO_ACTION:
+       case ACTION_NONE:
                break;
        default:
                BUG("action: %d", action);
@@ -1568,8 +1920,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                branch_name = options.head_name;
 
                } else {
-                       free(options.head_name);
-                       options.head_name = NULL;
+                       FREE_AND_NULL(options.head_name);
                        branch_name = "HEAD";
                }
                if (get_oid("HEAD", &options.orig_head))
@@ -1769,7 +2120,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
         * we just fast-forwarded.
         */
        strbuf_reset(&msg);
-       if (!oidcmp(&merge_base, &options.orig_head)) {
+       if (oideq(&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",
@@ -1792,7 +2143,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        options.revisions = revisions.buf;
 
 run_rebase:
-       ret = !!run_specific_rebase(&options);
+       ret = !!run_specific_rebase(&options, action);
 
 cleanup:
        strbuf_release(&revisions);
index 67f8978043a43988d653092f83f21bd5baad9751..caca11392713eb92816d1e503f286bfe0d9be78a 100644 (file)
@@ -14,7 +14,7 @@
 
 static int delta_base_offset = 1;
 static int pack_kept_objects = -1;
-static int write_bitmaps;
+static int write_bitmaps = -1;
 static int use_delta_islands;
 static char *packdir, *packtmp;
 
@@ -343,6 +343,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
            (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
                die(_("--keep-unreachable and -A are incompatible"));
 
+       if (write_bitmaps < 0)
+               write_bitmaps = (pack_everything & ALL_INTO_ONE) &&
+                                is_bare_repository();
        if (pack_kept_objects < 0)
                pack_kept_objects = write_bitmaps;
 
index f5701629a8a5698a64c91ca92226e6da72697441..644b21ca8d57a75fa83ecbf0e58dd07a685cd9e4 100644 (file)
@@ -370,16 +370,19 @@ static int replace_parents(struct strbuf *buf, int argc, const char **argv)
        /* prepare new parents */
        for (i = 0; i < argc; i++) {
                struct object_id oid;
+               struct commit *commit;
+
                if (get_oid(argv[i], &oid) < 0) {
                        strbuf_release(&new_parents);
                        return error(_("not a valid object name: '%s'"),
                                     argv[i]);
                }
-               if (!lookup_commit_reference(the_repository, &oid)) {
+               commit = lookup_commit_reference(the_repository, &oid);
+               if (!commit) {
                        strbuf_release(&new_parents);
-                       return error(_("could not parse %s"), argv[i]);
+                       return error(_("could not parse %s as a commit"), argv[i]);
                }
-               strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&oid));
+               strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&commit->object.oid));
        }
 
        /* replace existing parents with new ones */
@@ -478,15 +481,18 @@ static int create_graft(int argc, const char **argv, int force, int gentle)
 
        strbuf_release(&buf);
 
-       if (oideq(&old_oid, &new_oid)) {
+       if (oideq(&commit->object.oid, &new_oid)) {
                if (gentle) {
-                       warning(_("graft for '%s' unnecessary"), oid_to_hex(&old_oid));
+                       warning(_("graft for '%s' unnecessary"),
+                               oid_to_hex(&commit->object.oid));
                        return 0;
                }
-               return error(_("new commit is the same as the old one: '%s'"), oid_to_hex(&old_oid));
+               return error(_("new commit is the same as the old one: '%s'"),
+                            oid_to_hex(&commit->object.oid));
        }
 
-       return replace_object_oid(old_ref, &old_oid, "replacement", &new_oid, force);
+       return replace_object_oid(old_ref, &commit->object.oid,
+                                 "replacement", &new_oid, force);
 }
 
 static int convert_graft_file(int force)
index 425a5774db0697774df499fc16a626ab1e1efcf9..9f31837d303f1f302266fdd03298ddce73867ba0 100644 (file)
@@ -379,7 +379,6 @@ int cmd_rev_list(int argc, const char **argv, const char *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
@@ -409,6 +408,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (arg_missing_action)
+               revs.do_not_die_on_missing_tree = 1;
+
        argc = setup_revisions(argc, argv, &revs, &s_r_opt);
 
        memset(&info, 0, sizeof(info));
index a47b53ceaff92d4079b4ea73a61ce1552ad66b05..d4dcedbdc683f92d191cc6386f6388b1a918faa8 100644 (file)
@@ -96,11 +96,13 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
 {
        const char * const * usage_str = revert_or_cherry_pick_usage(opts);
        const char *me = action_name(opts);
+       const char *cleanup_arg = NULL;
        int cmd = 0;
        struct option base_options[] = {
                OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
                OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
                OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
+               OPT_CLEANUP(&cleanup_arg),
                OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
                OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
                OPT_NOOP_NOARG('r', NULL),
@@ -137,6 +139,11 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
        if (opts->keep_redundant_commits)
                opts->allow_empty = 1;
 
+       if (cleanup_arg) {
+               opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
+               opts->explicit_cleanup = 1;
+       }
+
        /* Check for incompatible command line arguments */
        if (cmd) {
                char *this_operation;
index db85b339823cc0aa5ab5d03220335d4e4240d656..90cbe896c99188130a36a18aa18a57cd080be591 100644 (file)
@@ -110,7 +110,7 @@ static int check_local_mod(struct object_id *head, int index_only)
                const struct cache_entry *ce;
                const char *name = list.entry[i].name;
                struct object_id oid;
-               unsigned mode;
+               unsigned short mode;
                int local_changes = 0;
                int staged_changes = 0;
 
diff --git a/builtin/serve.c b/builtin/serve.c
deleted file mode 100644 (file)
index d3fd240..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "cache.h"
-#include "builtin.h"
-#include "parse-options.h"
-#include "serve.h"
-
-static char const * const serve_usage[] = {
-       N_("git serve [<options>]"),
-       NULL
-};
-
-int cmd_serve(int argc, const char **argv, const char *prefix)
-{
-       struct serve_options opts = SERVE_OPTIONS_INIT;
-
-       struct option options[] = {
-               OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc,
-                        N_("quit after a single request/response exchange")),
-               OPT_BOOL(0, "advertise-capabilities", &opts.advertise_capabilities,
-                        N_("exit immediately after advertising capabilities")),
-               OPT_END()
-       };
-
-       /* ignore all unknown cmdline switches for now */
-       argc = parse_options(argc, argv, prefix, options, serve_usage,
-                            PARSE_OPT_KEEP_DASHDASH |
-                            PARSE_OPT_KEEP_UNKNOWN);
-       serve(&opts);
-
-       return 0;
-}
index 934e5149448783e32acc8b605f202384af7d96ee..082daeac329d1c9f8f94f91210f854468377ad7a 100644 (file)
@@ -753,7 +753,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                                /* Ah, that is a date spec... */
                                timestamp_t at;
                                at = approxidate(reflog_base);
-                               read_ref_at(ref, flags, at, -1, &oid, NULL,
+                               read_ref_at(get_main_ref_store(the_repository),
+                                           ref, flags, at, -1, &oid, NULL,
                                            NULL, NULL, &base);
                        }
                }
@@ -765,7 +766,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                        timestamp_t timestamp;
                        int tz;
 
-                       if (read_ref_at(ref, flags, 0, base + i, &oid, &logmsg,
+                       if (read_ref_at(get_main_ref_store(the_repository),
+                                       ref, flags, 0, base + i, &oid, &logmsg,
                                        &timestamp, &tz, NULL)) {
                                reflog = i;
                                break;
index 8c72ea864c81d8763ac48ee7e1e7795e1323ddb3..0bf4aa088e0ca4bf6378d121cfc23e0f42ac3af4 100644 (file)
@@ -1301,7 +1301,7 @@ static int add_possible_reference_from_superproject(
                                die(_("submodule '%s' cannot add alternate: %s"),
                                    sas->submodule_name, err.buf);
                        case SUBMODULE_ALTERNATE_ERROR_INFO:
-                               fprintf(stderr, _("submodule '%s' cannot add alternate: %s"),
+                               fprintf_ln(stderr, _("submodule '%s' cannot add alternate: %s"),
                                        sas->submodule_name, err.buf);
                        case SUBMODULE_ALTERNATE_ERROR_IGNORE:
                                ; /* nothing */
index ad97595fbfaa25c8ec44efec8ce5f946cf7de184..ef37dccf864932a31c019775cdad6d33207941a1 100644 (file)
 #include "ref-filter.h"
 
 static const char * const git_tag_usage[] = {
-       N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
+       N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
+               "\t\t<tagname> [<head>]"),
        N_("git tag -d <tagname>..."),
-       N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]"
-               "\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
+       N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n"
+               "\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
        N_("git tag -v [--format=<format>] <tagname>..."),
        NULL
 };
@@ -205,7 +206,14 @@ struct create_tag_options {
        } cleanup_mode;
 };
 
-static void create_tag(const struct object_id *object, const char *tag,
+static const char message_advice_nested_tag[] =
+       N_("You have created a nested tag. The object referred to by your new tag is\n"
+          "already a tag. If you meant to tag the object that it points to, use:\n"
+          "\n"
+          "\tgit tag -f %s %s^{}");
+
+static void create_tag(const struct object_id *object, const char *object_ref,
+                      const char *tag,
                       struct strbuf *buf, struct create_tag_options *opt,
                       struct object_id *prev, struct object_id *result)
 {
@@ -215,7 +223,10 @@ static void create_tag(const struct object_id *object, const char *tag,
 
        type = oid_object_info(the_repository, object, NULL);
        if (type <= OBJ_NONE)
-           die(_("bad object type."));
+               die(_("bad object type."));
+
+       if (type == OBJ_TAG && advice_nested_tag)
+               advise(_(message_advice_nested_tag), tag, object_ref);
 
        strbuf_addf(&header,
                    "object %s\n"
@@ -397,8 +408,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
                OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
                OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
-               OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"),
-                       N_("how to strip spaces and #comments from message")),
+               OPT_CLEANUP(&cleanup_arg),
                OPT_STRING('u', "local-user", &keyid, N_("key-id"),
                                        N_("use another key to sign the tag")),
                OPT__FORCE(&force, N_("replace the tag if exists"), 0),
@@ -549,7 +559,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (create_tag_object) {
                if (force_sign_annotate && !annotate)
                        opt.sign = 1;
-               create_tag(&object, tag, &buf, &opt, &prev, &object);
+               create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object);
        }
 
        transaction = ref_transaction_begin(&err);
index 890c251d665a1b6800b87cc6ee0fcb04a3f9f538..27db0928bf052b0c518bee57ffa7cdd6c03d5d85 100644 (file)
@@ -597,7 +597,7 @@ static struct cache_entry *read_one_ent(const char *which,
                                        struct object_id *ent, const char *path,
                                        int namelen, int stage)
 {
-       unsigned mode;
+       unsigned short mode;
        struct object_id oid;
        struct cache_entry *ce;
 
index f438f93811bfc62aa79a9ba6f9476240100e286f..b26f3dc3b74cb9ccb4ea630fafe8cd0cdcf5a012 100644 (file)
@@ -6,11 +6,11 @@
 
 #include "cache.h"
 
-extern int index_bulk_checkin(struct object_id *oid,
-                             int fd, size_t size, enum object_type type,
-                             const char *path, unsigned flags);
+int index_bulk_checkin(struct object_id *oid,
+                      int fd, size_t size, enum object_type type,
+                      const char *path, unsigned flags);
 
-extern void plug_bulk_checkin(void);
-extern void unplug_bulk_checkin(void);
+void plug_bulk_checkin(void);
+void unplug_bulk_checkin(void);
 
 #endif
diff --git a/cache.h b/cache.h
index e928fe9d3bd90b3ac2ca69bd31d281784287d536..fa8ede9a2d6441842f49740482bb74f6c23c1a5d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -352,10 +352,10 @@ struct index_state {
 };
 
 /* Name hashing */
-extern int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
-extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
-extern void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
-extern void free_name_hash(struct index_state *istate);
+int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
+void add_name_hash(struct index_state *istate, struct cache_entry *ce);
+void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
+void free_name_hash(struct index_state *istate);
 
 
 /* Cache entry creation and cleanup */
@@ -545,7 +545,7 @@ static inline enum object_type object_type(unsigned int mode)
  */
 extern const char * const local_repo_env[];
 
-extern void setup_git_env(const char *git_dir);
+void setup_git_env(const char *git_dir);
 
 /*
  * Returns true iff we have a configured git repository (either via
@@ -554,29 +554,29 @@ extern void setup_git_env(const char *git_dir);
 int have_git_dir(void);
 
 extern int is_bare_repository_cfg;
-extern int is_bare_repository(void);
-extern int is_inside_git_dir(void);
+int is_bare_repository(void);
+int is_inside_git_dir(void);
 extern char *git_work_tree_cfg;
-extern int is_inside_work_tree(void);
-extern const char *get_git_dir(void);
-extern const char *get_git_common_dir(void);
-extern char *get_object_directory(void);
-extern char *get_index_file(void);
-extern char *get_graft_file(struct repository *r);
-extern void set_git_dir(const char *path);
-extern int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
-extern int get_common_dir(struct strbuf *sb, const char *gitdir);
-extern const char *get_git_namespace(void);
-extern const char *strip_namespace(const char *namespaced_ref);
-extern const char *get_super_prefix(void);
-extern const char *get_git_work_tree(void);
+int is_inside_work_tree(void);
+const char *get_git_dir(void);
+const char *get_git_common_dir(void);
+char *get_object_directory(void);
+char *get_index_file(void);
+char *get_graft_file(struct repository *r);
+void set_git_dir(const char *path);
+int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
+int get_common_dir(struct strbuf *sb, const char *gitdir);
+const char *get_git_namespace(void);
+const char *strip_namespace(const char *namespaced_ref);
+const char *get_super_prefix(void);
+const char *get_git_work_tree(void);
 
 /*
  * Return true if the given path is a git directory; note that this _just_
  * looks at the directory itself. If you want to know whether "foo/.git"
  * is a repository, you must feed that path, not just "foo".
  */
-extern int is_git_directory(const char *path);
+int is_git_directory(const char *path);
 
 /*
  * Return 1 if the given path is the root of a git repository or
@@ -588,7 +588,7 @@ extern int is_git_directory(const char *path);
  * as we usually consider sub-repos precious, and would prefer to err on the
  * side of not disrupting or deleting them.
  */
-extern int is_nonbare_repository_dir(struct strbuf *path);
+int is_nonbare_repository_dir(struct strbuf *path);
 
 #define READ_GITFILE_ERR_STAT_FAILED 1
 #define READ_GITFILE_ERR_NOT_A_FILE 2
@@ -598,17 +598,17 @@ extern int is_nonbare_repository_dir(struct strbuf *path);
 #define READ_GITFILE_ERR_NO_PATH 6
 #define READ_GITFILE_ERR_NOT_A_REPO 7
 #define READ_GITFILE_ERR_TOO_LARGE 8
-extern void read_gitfile_error_die(int error_code, const char *path, const char *dir);
-extern const char *read_gitfile_gently(const char *path, int *return_error_code);
+void read_gitfile_error_die(int error_code, const char *path, const char *dir);
+const char *read_gitfile_gently(const char *path, int *return_error_code);
 #define read_gitfile(path) read_gitfile_gently((path), NULL)
-extern const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
+const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
 #define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
 
-extern void set_git_work_tree(const char *tree);
+void set_git_work_tree(const char *tree);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
-extern void setup_work_tree(void);
+void setup_work_tree(void);
 /*
  * Find the commondir and gitdir of the repository that contains the current
  * working directory, without changing the working directory or other global
@@ -617,12 +617,12 @@ extern void setup_work_tree(void);
  * both have the same result appended to the buffer.  The return value is
  * either 0 upon success and non-zero if no repository was found.
  */
-extern int discover_git_directory(struct strbuf *commondir,
-                                 struct strbuf *gitdir);
-extern const char *setup_git_directory_gently(int *);
-extern const char *setup_git_directory(void);
-extern char *prefix_path(const char *prefix, int len, const char *path);
-extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
+int discover_git_directory(struct strbuf *commondir,
+                          struct strbuf *gitdir);
+const char *setup_git_directory_gently(int *);
+const char *setup_git_directory(void);
+char *prefix_path(const char *prefix, int len, const char *path);
+char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
 
 /*
  * Concatenate "prefix" (if len is non-zero) and "path", with no
@@ -634,23 +634,23 @@ extern char *prefix_path_gently(const char *prefix, int len, int *remaining, con
  * The return value is always a newly allocated string (even if the
  * prefix was empty).
  */
-extern char *prefix_filename(const char *prefix, const char *path);
+char *prefix_filename(const char *prefix, const char *path);
 
-extern int check_filename(const char *prefix, const char *name);
-extern void verify_filename(const char *prefix,
-                           const char *name,
-                           int diagnose_misspelt_rev);
-extern void verify_non_filename(const char *prefix, const char *name);
-extern int path_inside_repo(const char *prefix, const char *path);
+int check_filename(const char *prefix, const char *name);
+void verify_filename(const char *prefix,
+                    const char *name,
+                    int diagnose_misspelt_rev);
+void verify_non_filename(const char *prefix, const char *name);
+int path_inside_repo(const char *prefix, const char *path);
 
 #define INIT_DB_QUIET 0x0001
 #define INIT_DB_EXIST_OK 0x0002
 
-extern int init_db(const char *git_dir, const char *real_git_dir,
-                  const char *template_dir, unsigned int flags);
+int init_db(const char *git_dir, const char *real_git_dir,
+           const char *template_dir, unsigned int flags);
 
-extern void sanitize_stdfds(void);
-extern int daemonize(void);
+void sanitize_stdfds(void);
+int daemonize(void);
 
 #define alloc_nr(x) (((x)+16)*3/2)
 
@@ -674,14 +674,14 @@ extern int daemonize(void);
 
 /* Initialize and use the cache information */
 struct lock_file;
-extern void preload_index(struct index_state *index,
-                         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,
-                          const char *gitdir);
-extern int is_index_unborn(struct index_state *);
+void preload_index(struct index_state *index,
+                  const struct pathspec *pathspec,
+                  unsigned int refresh_flags);
+int do_read_index(struct index_state *istate, const char *path,
+                 int must_exist); /* for testting only! */
+int read_index_from(struct index_state *, const char *path,
+                   const char *gitdir);
+int is_index_unborn(struct index_state *);
 
 /* For use with `write_locked_index()`. */
 #define COMMIT_LOCK            (1 << 0)
@@ -706,11 +706,11 @@ extern int is_index_unborn(struct index_state *);
  * If `SKIP_IF_UNCHANGED` is given and the index is unchanged, nothing
  * is written (and the lock is rolled back if `COMMIT_LOCK` is given).
  */
-extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
+int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
 
-extern int discard_index(struct index_state *);
-extern void move_index_extensions(struct index_state *dst, struct index_state *src);
-extern int unmerged_index(const struct index_state *);
+int discard_index(struct index_state *);
+void move_index_extensions(struct index_state *dst, struct index_state *src);
+int unmerged_index(const struct index_state *);
 
 /**
  * Returns 1 if istate differs from tree, 0 otherwise.  If tree is NULL,
@@ -719,15 +719,15 @@ extern int unmerged_index(const struct index_state *);
  * provided, the space-separated list of files that differ will be appended
  * to it.
  */
-extern int repo_index_has_changes(struct repository *repo,
-                                 struct tree *tree,
-                                 struct strbuf *sb);
+int repo_index_has_changes(struct repository *repo,
+                          struct tree *tree,
+                          struct strbuf *sb);
 
-extern int verify_path(const char *path, unsigned mode);
-extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
-extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
-extern void adjust_dirname_case(struct index_state *istate, char *name);
-extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
+int verify_path(const char *path, unsigned mode);
+int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
+int index_dir_exists(struct index_state *istate, const char *name, int namelen);
+void adjust_dirname_case(struct index_state *istate, char *name);
+struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 
 /*
  * Searches for an entry defined by name and namelen in the given index.
@@ -746,7 +746,7 @@ extern struct cache_entry *index_file_exists(struct index_state *istate, const c
  * index_name_pos(&index, "f", 1) -> -3
  * index_name_pos(&index, "g", 1) -> -5
  */
-extern int index_name_pos(const struct index_state *, const char *name, int namelen);
+int index_name_pos(const struct index_state *, const char *name, int namelen);
 
 #define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
@@ -755,14 +755,14 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
 #define ADD_CACHE_NEW_ONLY 16          /* Do not replace existing ones */
 #define ADD_CACHE_KEEP_CACHE_TREE 32   /* Do not invalidate cache-tree */
 #define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
-extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
-extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
+int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
+void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 
 /* Remove entry, return true if there are more entries to go. */
-extern int remove_index_entry_at(struct index_state *, int pos);
+int remove_index_entry_at(struct index_state *, int pos);
 
-extern void remove_marked_cache_entries(struct index_state *istate, int invalidate);
-extern int remove_file_from_index(struct index_state *, const char *path);
+void remove_marked_cache_entries(struct index_state *istate, int invalidate);
+int remove_file_from_index(struct index_state *, const char *path);
 #define ADD_CACHE_VERBOSE 1
 #define ADD_CACHE_PRETEND 2
 #define ADD_CACHE_IGNORE_ERRORS        4
@@ -777,14 +777,14 @@ extern int remove_file_from_index(struct index_state *, const char *path);
  * the latter will do necessary lstat(2) internally before
  * calling the former.
  */
-extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
-extern int add_file_to_index(struct index_state *, const char *path, int flags);
+int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
+int add_file_to_index(struct index_state *, const char *path, int flags);
 
-extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
-extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
-extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
-extern int index_name_is_other(const struct index_state *, const char *, int);
-extern void *read_blob_data_from_index(const struct index_state *, const char *, unsigned long *);
+int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
+int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
+void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
+int index_name_is_other(const struct index_state *, const char *, int);
+void *read_blob_data_from_index(const struct index_state *, const char *, unsigned long *);
 
 /* do stat comparison even if CE_VALID is true */
 #define CE_MATCH_IGNORE_VALID          01
@@ -798,22 +798,22 @@ 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);
+int is_racy_timestamp(const struct index_state *istate,
+                     const struct cache_entry *ce);
+int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+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 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);
+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 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
  * might have changed.
  */
-extern void fill_stat_data(struct stat_data *sd, struct stat *st);
+void fill_stat_data(struct stat_data *sd, struct stat *st);
 
 /*
  * Return 0 if st is consistent with a file not having been changed
@@ -821,11 +821,11 @@ extern void fill_stat_data(struct stat_data *sd, struct stat *st);
  * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
  * INODE_CHANGED, and DATA_CHANGED.
  */
-extern int match_stat_data(const struct stat_data *sd, struct stat *st);
-extern int match_stat_data_racy(const struct index_state *istate,
-                               const struct stat_data *sd, struct stat *st);
+int match_stat_data(const struct stat_data *sd, struct stat *st);
+int match_stat_data_racy(const struct index_state *istate,
+                        const struct stat_data *sd, struct stat *st);
 
-extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
+void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
 #define REFRESH_REALLY         0x0001  /* ignore_valid */
 #define REFRESH_UNMERGED       0x0002  /* allow unmerged */
@@ -834,10 +834,10 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 #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);
+int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
+struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
 
-extern void set_alternate_index_output(const char *);
+void set_alternate_index_output(const char *);
 
 extern int verify_index_checksum;
 extern int verify_ce_order;
@@ -1022,7 +1022,7 @@ int verify_repository_format(const struct repository_format *format,
  * set_git_dir() before calling this, and use it only for "are we in a valid
  * repo?".
  */
-extern void check_repository_format(void);
+void check_repository_format(void);
 
 #define MTIME_CHANGED  0x0001
 #define CTIME_CHANGED  0x0002
@@ -1047,8 +1047,10 @@ extern void check_repository_format(void);
  * Note that while this version avoids the static buffer, it is not fully
  * reentrant, as it calls into other non-reentrant git code.
  */
-extern const char *find_unique_abbrev(const struct object_id *oid, int len);
-extern int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len);
+const char *repo_find_unique_abbrev(struct repository *r, const struct object_id *oid, int len);
+#define find_unique_abbrev(oid, len) repo_find_unique_abbrev(the_repository, oid, len)
+int repo_find_unique_abbrev_r(struct repository *r, char *hex, const struct object_id *oid, int len);
+#define find_unique_abbrev_r(hex, oid, len) repo_find_unique_abbrev_r(the_repository, hex, oid, len)
 
 extern const unsigned char null_sha1[GIT_MAX_RAWSZ];
 extern const struct object_id null_oid;
@@ -1244,7 +1246,7 @@ typedef int create_file_fn(const char *path, void *cb);
 int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
 
 int mkdir_in_gitdir(const char *path);
-extern char *expand_user_path(const char *path, int real_home);
+char *expand_user_path(const char *path, int real_home);
 const char *enter_repo(const char *path, int strict);
 static inline int is_absolute_path(const char *path)
 {
@@ -1288,26 +1290,26 @@ int looks_like_command_line_option(const char *str);
  * "$XDG_CONFIG_HOME/git/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
  * "$HOME/.config/git/$filename". Return NULL upon error.
  */
-extern char *xdg_config_home(const char *filename);
+char *xdg_config_home(const char *filename);
 
 /**
  * Return a newly allocated string with the evaluation of
  * "$XDG_CACHE_HOME/git/$filename" if $XDG_CACHE_HOME is non-empty, otherwise
  * "$HOME/.cache/git/$filename". Return NULL upon error.
  */
-extern char *xdg_cache_home(const char *filename);
+char *xdg_cache_home(const char *filename);
 
-extern int git_open_cloexec(const char *name, int flags);
+int git_open_cloexec(const char *name, int flags);
 #define git_open(name) git_open_cloexec(name, O_RDONLY)
-extern int unpack_loose_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
-extern int parse_loose_header(const char *hdr, unsigned long *sizep);
+int unpack_loose_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
+int parse_loose_header(const char *hdr, unsigned long *sizep);
 
-extern int check_object_signature(const struct object_id *oid, void *buf, unsigned long size, const char *type);
+int check_object_signature(const struct object_id *oid, void *buf, unsigned long size, const char *type);
 
-extern int finalize_object_file(const char *tmpfile, const char *filename);
+int finalize_object_file(const char *tmpfile, const char *filename);
 
 /* Helper to check and "touch" a file */
-extern int check_and_freshen_file(const char *fn, int freshen);
+int check_and_freshen_file(const char *fn, int freshen);
 
 extern const signed char hexval_table[256];
 static inline unsigned int hexval(unsigned char c)
@@ -1333,7 +1335,7 @@ static inline int hex2chr(const char *s)
 #define FALLBACK_DEFAULT_ABBREV 7
 
 struct object_context {
-       unsigned mode;
+       unsigned short mode;
        /*
         * symlink_path is only used by get_tree_entry_follow_symlinks,
         * and only for symlinks that point outside the repository.
@@ -1380,22 +1382,34 @@ enum get_oid_result {
                       */
 };
 
-extern int get_oid(const char *str, struct object_id *oid);
-extern int get_oidf(struct object_id *oid, const char *fmt, ...);
-extern int get_oid_commit(const char *str, struct object_id *oid);
-extern int get_oid_committish(const char *str, struct object_id *oid);
-extern int get_oid_tree(const char *str, struct object_id *oid);
-extern int get_oid_treeish(const char *str, struct object_id *oid);
-extern int get_oid_blob(const char *str, struct object_id *oid);
-extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
-extern enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
-                               unsigned flags, struct object_id *oid,
-                               struct object_context *oc);
+int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
+int get_oidf(struct object_id *oid, const char *fmt, ...);
+int repo_get_oid_commit(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_committish(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_tree(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_treeish(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_blob(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_mb(struct repository *r, const char *str, struct object_id *oid);
+void maybe_die_on_misspelt_object_name(struct repository *repo,
+                                      const char *name,
+                                      const char *prefix);
+enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
+                                        unsigned flags, struct object_id *oid,
+                                        struct object_context *oc);
+
+#define get_oid(str, oid)              repo_get_oid(the_repository, str, oid)
+#define get_oid_commit(str, oid)       repo_get_oid_commit(the_repository, str, oid)
+#define get_oid_committish(str, oid)   repo_get_oid_committish(the_repository, str, oid)
+#define get_oid_tree(str, oid)         repo_get_oid_tree(the_repository, str, oid)
+#define get_oid_treeish(str, oid)      repo_get_oid_treeish(the_repository, str, oid)
+#define get_oid_blob(str, oid)         repo_get_oid_blob(the_repository, str, oid)
+#define get_oid_mb(str, oid)           repo_get_oid_mb(the_repository, str, oid)
 
 typedef int each_abbrev_fn(const struct object_id *oid, void *);
-extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
+int repo_for_each_abbrev(struct repository *r, const char *prefix, each_abbrev_fn, void *);
+#define for_each_abbrev(prefix, fn, data) repo_for_each_abbrev(the_repository, prefix, fn, data)
 
-extern int set_disambiguate_hint_config(const char *var, const char *value);
+int set_disambiguate_hint_config(const char *var, const char *value);
 
 /*
  * Try to read a SHA1 in hexadecimal format from the 40 characters
@@ -1404,15 +1418,15 @@ extern int set_disambiguate_hint_config(const char *var, const char *value);
  * input, so it is safe to pass this function an arbitrary
  * null-terminated string.
  */
-extern int get_sha1_hex(const char *hex, unsigned char *sha1);
-extern int get_oid_hex(const char *hex, struct object_id *sha1);
+int get_sha1_hex(const char *hex, unsigned char *sha1);
+int get_oid_hex(const char *hex, struct object_id *sha1);
 
 /*
  * Read `len` pairs of hexadecimal digits from `hex` and write the
  * values to `binary` as `len` bytes. Return 0 on success, or -1 if
  * the input does not consist of hex digits).
  */
-extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
+int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 
 /*
  * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
@@ -1440,7 +1454,7 @@ char *oid_to_hex(const struct object_id *oid);                                            /* same static buffer */
  * other invalid character.  end is only updated on success; otherwise, it is
  * unmodified.
  */
-extern int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
+int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
 
 /*
  * This reads short-hand syntax that not only evaluates to a commit
@@ -1471,24 +1485,30 @@ extern int parse_oid_hex(const char *hex, struct object_id *oid, const char **en
 #define INTERPRET_BRANCH_LOCAL (1<<0)
 #define INTERPRET_BRANCH_REMOTE (1<<1)
 #define INTERPRET_BRANCH_HEAD (1<<2)
-extern int interpret_branch_name(const char *str, int len, struct strbuf *,
-                                unsigned allowed);
-extern int get_oid_mb(const char *str, struct object_id *oid);
-
-extern int validate_headref(const char *ref);
-
-extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
-extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
-extern int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
-extern int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
-
-extern void *read_object_with_reference(const struct object_id *oid,
-                                       const char *required_type,
-                                       unsigned long *size,
-                                       struct object_id *oid_ret);
-
-extern struct object *peel_to_type(const char *name, int namelen,
-                                  struct object *o, enum object_type);
+int repo_interpret_branch_name(struct repository *r,
+                              const char *str, int len,
+                              struct strbuf *buf,
+                              unsigned allowed);
+#define interpret_branch_name(str, len, buf, allowed) \
+       repo_interpret_branch_name(the_repository, str, len, buf, allowed)
+
+int validate_headref(const char *ref);
+
+int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
+int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
+int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
+int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
+
+void *read_object_with_reference(const struct object_id *oid,
+                                const char *required_type,
+                                unsigned long *size,
+                                struct object_id *oid_ret);
+
+struct object *repo_peel_to_type(struct repository *r,
+                                const char *name, int namelen,
+                                struct object *o, enum object_type);
+#define peel_to_type(name, namelen, obj, type) \
+       repo_peel_to_type(the_repository, name, namelen, obj, type)
 
 enum date_mode_type {
        DATE_NORMAL = 0,
@@ -1542,24 +1562,24 @@ enum want_ident {
        WANT_COMMITTER_IDENT
 };
 
-extern const char *git_author_info(int);
-extern const char *git_committer_info(int);
-extern const char *fmt_ident(const char *name, const char *email,
-               enum want_ident whose_ident,
-               const char *date_str, int);
-extern const char *fmt_name(enum want_ident);
-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 *);
+const char *git_author_info(int);
+const char *git_committer_info(int);
+const char *fmt_ident(const char *name, const char *email,
+                     enum want_ident whose_ident,
+                     const char *date_str, int);
+const char *fmt_name(enum want_ident);
+const char *ident_default_name(void);
+const char *ident_default_email(void);
+const char *git_editor(void);
+const char *git_sequence_editor(void);
+const char *git_pager(int stdout_is_tty);
+int is_terminal_dumb(void);
+int git_ident_config(const char *, const char *, void *);
 /*
  * Prepare an ident to fall back on if the user didn't configure it.
  */
 void prepare_fallback_ident(const char *name, const char *email);
-extern void reset_ident_date(void);
+void reset_ident_date(void);
 
 struct ident_split {
        const char *name_begin;
@@ -1575,7 +1595,7 @@ struct ident_split {
  * Signals an success with 0, but time part of the result may be NULL
  * if the input lacks timestamp and zone
  */
-extern int split_ident_line(struct ident_split *, const char *, int);
+int split_ident_line(struct ident_split *, const char *, int);
 
 /*
  * Like show_date, but pull the timestamp and tz parameters from
@@ -1592,7 +1612,7 @@ const char *show_ident_date(const struct ident_split *id,
  * Because there are two fields, we must choose one as the primary key; we
  * currently arbitrarily pick the email.
  */
-extern int ident_cmp(const struct ident_split *, const struct ident_split *);
+int ident_cmp(const struct ident_split *, const struct ident_split *);
 
 struct checkout {
        struct index_state *istate;
@@ -1608,14 +1628,14 @@ struct checkout {
 #define CHECKOUT_INIT { NULL, "" }
 
 #define TEMPORARY_FILENAME_LENGTH 25
-extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);
-extern void enable_delayed_checkout(struct checkout *state);
-extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
+int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);
+void enable_delayed_checkout(struct checkout *state);
+int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
 /*
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
  */
-extern void unlink_entry(const struct cache_entry *ce);
+void unlink_entry(const struct cache_entry *ce);
 
 struct cache_def {
        struct strbuf path;
@@ -1629,12 +1649,12 @@ static inline void cache_def_clear(struct cache_def *cache)
        strbuf_release(&cache->path);
 }
 
-extern int has_symlink_leading_path(const char *name, int len);
-extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
-extern int check_leading_path(const char *name, int len);
-extern int has_dirs_only_path(const char *name, int len, int prefix_len);
-extern void schedule_dir_for_removal(const char *name, int len);
-extern void remove_scheduled_dirs(void);
+int has_symlink_leading_path(const char *name, int len);
+int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
+int check_leading_path(const char *name, int len);
+int has_dirs_only_path(const char *name, int len, int prefix_len);
+void schedule_dir_for_removal(const char *name, int len);
+void remove_scheduled_dirs(void);
 
 struct pack_window {
        struct pack_window *next;
@@ -1656,14 +1676,14 @@ struct pack_entry {
  * usual "XXXXXX" trailer, and the resulting filename is written into the
  * "template" buffer. Returns the open descriptor.
  */
-extern int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
 
 /*
  * Create a pack .keep file named "name" (which should generally be the output
  * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
  * error.
  */
-extern int odb_pack_keep(const char *name);
+int odb_pack_keep(const char *name);
 
 /*
  * Set this to 0 to prevent oid_object_info_extended() from fetching missing
@@ -1674,10 +1694,10 @@ extern int odb_pack_keep(const char *name);
 extern int fetch_if_missing;
 
 /* Dumb servers support */
-extern int update_server_info(int);
+int update_server_info(int);
 
-extern const char *get_log_output_encoding(void);
-extern const char *get_commit_output_encoding(void);
+const char *get_log_output_encoding(void);
+const char *get_commit_output_encoding(void);
 
 /*
  * This is a hack for test programs like test-dump-untracked-cache to
@@ -1686,8 +1706,8 @@ extern const char *get_commit_output_encoding(void);
  */
 extern int ignore_untracked_cache_config;
 
-extern int committer_ident_sufficiently_given(void);
-extern int author_ident_sufficiently_given(void);
+int committer_ident_sufficiently_given(void);
+int author_ident_sufficiently_given(void);
 
 extern const char *git_commit_encoding;
 extern const char *git_log_output_encoding;
@@ -1695,22 +1715,22 @@ extern const char *git_mailmap_file;
 extern const char *git_mailmap_blob;
 
 /* IO helper functions */
-extern void maybe_flush_or_die(FILE *, const char *);
+void maybe_flush_or_die(FILE *, const char *);
 __attribute__((format (printf, 2, 3)))
-extern void fprintf_or_die(FILE *, const char *fmt, ...);
+void fprintf_or_die(FILE *, const char *fmt, ...);
 
 #define COPY_READ_ERROR (-2)
 #define COPY_WRITE_ERROR (-3)
-extern int copy_fd(int ifd, int ofd);
-extern int copy_file(const char *dst, const char *src, int mode);
-extern int copy_file_with_time(const char *dst, const char *src, int mode);
+int copy_fd(int ifd, int ofd);
+int copy_file(const char *dst, const char *src, int mode);
+int copy_file_with_time(const char *dst, const char *src, int mode);
 
-extern void write_or_die(int fd, const void *buf, size_t count);
-extern void fsync_or_die(int fd, const char *);
+void write_or_die(int fd, const void *buf, size_t count);
+void fsync_or_die(int fd, const char *);
 
-extern ssize_t read_in_full(int fd, void *buf, size_t count);
-extern ssize_t write_in_full(int fd, const void *buf, size_t count);
-extern ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
+ssize_t read_in_full(int fd, void *buf, size_t count);
+ssize_t write_in_full(int fd, const void *buf, size_t count);
+ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
 
 static inline ssize_t write_str_in_full(int fd, const char *str)
 {
@@ -1721,7 +1741,7 @@ static inline ssize_t write_str_in_full(int fd, const char *str)
  * Open (and truncate) the file at path, write the contents of buf to it,
  * and close it. Dies if any errors are encountered.
  */
-extern void write_file_buf(const char *path, const char *buf, size_t len);
+void write_file_buf(const char *path, const char *buf, size_t len);
 
 /**
  * Like write_file_buf(), but format the contents into a buffer first.
@@ -1731,16 +1751,16 @@ extern void write_file_buf(const char *path, const char *buf, size_t len);
  *   write_file(path, "counter: %d", ctr);
  */
 __attribute__((format (printf, 2, 3)))
-extern void write_file(const char *path, const char *fmt, ...);
+void write_file(const char *path, const char *fmt, ...);
 
 /* pager.c */
-extern void setup_pager(void);
-extern int pager_in_use(void);
+void setup_pager(void);
+int pager_in_use(void);
 extern int pager_use_color;
-extern int term_columns(void);
-extern int decimal_width(uintmax_t);
-extern int check_pager_config(const char *cmd);
-extern void prepare_pager_args(struct child_process *, const char *pager);
+int term_columns(void);
+int decimal_width(uintmax_t);
+int check_pager_config(const char *cmd);
+void prepare_pager_args(struct child_process *, const char *pager);
 
 extern const char *editor_program;
 extern const char *askpass_program;
@@ -1784,13 +1804,13 @@ 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(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);
-extern char *whitespace_error_string(unsigned ws);
-extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
-extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
+unsigned whitespace_rule(struct index_state *, const char *);
+unsigned parse_whitespace_rule(const char *);
+unsigned ws_check(const char *line, int len, unsigned ws_rule);
+void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
+char *whitespace_error_string(unsigned ws);
+void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
+int ws_blank_line(const char *line, int len, unsigned ws_rule);
 #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
 
 /* ls-files */
@@ -1860,9 +1880,9 @@ void safe_create_dir(const char *dir, int share);
  * Should we print an ellipsis after an abbreviated SHA-1 value
  * when doing diff-raw output or indicating a detached HEAD?
  */
-extern int print_sha1_ellipsis(void);
+int print_sha1_ellipsis(void);
 
 /* Return 1 if the file is empty or does not exists, 0 otherwise. */
-extern int is_empty_or_missing_file(const char *filename);
+int is_empty_or_missing_file(const char *filename);
 
 #endif /* CACHE_H */
index 6b2073310c4a6a735a557ab0883c7bb045c2c458..1152133bd77a773f40afd6c3477224e051318e0c 100644 (file)
@@ -8,8 +8,8 @@
  * tracking branch.  Return the name of the remote if such a branch
  * exists, NULL otherwise.
  */
-extern const char *unique_tracking_name(const char *name,
-                                       struct object_id *oid,
-                                       int *dwim_remotes_matched);
+const char *unique_tracking_name(const char *name,
+                                struct object_id *oid,
+                                int *dwim_remotes_matched);
 
 #endif /* CHECKOUT_H */
index 2567a5cf4dff99bcaf0e507168e1d4cbaba05bce..448c7440b3f0eb6a3c7e86fb775e3506b079b452 100644 (file)
--- a/column.h
+++ b/column.h
@@ -27,20 +27,20 @@ struct column_options {
 };
 
 struct option;
-extern int parseopt_column_callback(const struct option *, const char *, int);
-extern int git_column_config(const char *var, const char *value,
-                            const char *command, unsigned int *colopts);
-extern int finalize_colopts(unsigned int *colopts, int stdout_is_tty);
+int parseopt_column_callback(const struct option *, const char *, int);
+int git_column_config(const char *var, const char *value,
+                     const char *command, unsigned int *colopts);
+int finalize_colopts(unsigned int *colopts, int stdout_is_tty);
 static inline int column_active(unsigned int colopts)
 {
        return (colopts & COL_ENABLE_MASK) == COL_ENABLED;
 }
 
 struct string_list;
-extern void print_columns(const struct string_list *list, unsigned int colopts,
-                         const struct column_options *opts);
+void print_columns(const struct string_list *list, unsigned int colopts,
+                  const struct column_options *opts);
 
-extern int run_column_filter(int colopts, const struct column_options *);
-extern int stop_column_filter(void);
+int run_column_filter(int colopts, const struct column_options *);
+int stop_column_filter(void);
 
 #endif
index 66865acbd7489df4f85ee2454fe05a9b346e7aa7..7bcc9bb7e0f9965a2ab3176c0416943430c6a6aa 100644 (file)
@@ -397,6 +397,11 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
        item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
 }
 
+static inline void set_commit_tree(struct commit *c, struct tree *t)
+{
+       c->maybe_tree = t;
+}
+
 static int fill_commit_in_graph(struct repository *r,
                                struct commit *item,
                                struct commit_graph *g, uint32_t pos)
@@ -410,7 +415,7 @@ static int fill_commit_in_graph(struct repository *r,
        item->object.parsed = 1;
        item->graph_pos = pos;
 
-       item->maybe_tree = NULL;
+       set_commit_tree(item, NULL);
 
        date_high = get_be32(commit_data + g->hash_len + 8) & 0x3;
        date_low = get_be32(commit_data + g->hash_len + 12);
@@ -496,7 +501,7 @@ static struct tree *load_tree_for_commit(struct repository *r,
                                           GRAPH_DATA_WIDTH * (c->graph_pos);
 
        hashcpy(oid.hash, commit_data);
-       c->maybe_tree = lookup_tree(r, &oid);
+       set_commit_tree(c, lookup_tree(r, &oid));
 
        return c->maybe_tree;
 }
index a5333c7ac6c373a13f9298b36be5ff94a90a3e3f..8fa1883c61c580578a755cdf2da009203d8d386e 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -340,15 +340,21 @@ void free_commit_buffer(struct parsed_object_pool *pool, struct commit *commit)
        }
 }
 
-struct tree *get_commit_tree(const struct commit *commit)
+static inline void set_commit_tree(struct commit *c, struct tree *t)
+{
+       c->maybe_tree = t;
+}
+
+struct tree *repo_get_commit_tree(struct repository *r,
+                                 const struct commit *commit)
 {
        if (commit->maybe_tree || !commit->object.parsed)
                return commit->maybe_tree;
 
-       if (commit->graph_pos == COMMIT_NOT_FROM_GRAPH)
-               BUG("commit has NULL tree, but was not loaded from commit-graph");
+       if (commit->graph_pos != COMMIT_NOT_FROM_GRAPH)
+               return get_commit_tree_in_graph(r, commit);
 
-       return get_commit_tree_in_graph(the_repository, commit);
+       return NULL;
 }
 
 struct object_id *get_commit_tree_oid(const struct commit *commit)
@@ -358,7 +364,7 @@ struct object_id *get_commit_tree_oid(const struct commit *commit)
 
 void release_commit_memory(struct parsed_object_pool *pool, struct commit *c)
 {
-       c->maybe_tree = NULL;
+       set_commit_tree(c, NULL);
        c->index = 0;
        free_commit_buffer(pool, c);
        free_commit_list(c->parents);
@@ -406,7 +412,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
        if (get_oid_hex(bufptr + 5, &parent) < 0)
                return error("bad tree pointer in commit %s",
                             oid_to_hex(&item->object.oid));
-       item->maybe_tree = lookup_tree(r, &parent);
+       set_commit_tree(item, lookup_tree(r, &parent));
        bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
 
index 8f1f39f4c39b05327ee342018091d847ea7ee5c4..f5295ca7f3efe9b7665d1c6728814bfe2901d059 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -32,7 +32,7 @@ struct commit {
 
        /*
         * If the commit is loaded from the commit-graph file, then this
-        * member may be NULL. Only access it through get_commit_tree()
+        * member may be NULL. Only access it through repo_get_commit_tree()
         * or get_commit_tree_oid().
         */
        struct tree *maybe_tree;
@@ -149,7 +149,8 @@ void repo_unuse_commit_buffer(struct repository *r,
  */
 void free_commit_buffer(struct parsed_object_pool *pool, struct commit *);
 
-struct tree *get_commit_tree(const struct commit *);
+struct tree *repo_get_commit_tree(struct repository *, const struct commit *);
+#define get_commit_tree(c) repo_get_commit_tree(the_repository, c)
 struct object_id *get_commit_tree_oid(const struct commit *);
 
 /*
@@ -183,10 +184,10 @@ void free_commit_list(struct commit_list *list);
 
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
 
-extern int has_non_ascii(const char *text);
-extern const char *logmsg_reencode(const struct commit *commit,
-                                  char **commit_encoding,
-                                  const char *output_encoding);
+int has_non_ascii(const char *text);
+const char *logmsg_reencode(const struct commit *commit,
+                           char **commit_encoding,
+                           const char *output_encoding);
 const char *repo_logmsg_reencode(struct repository *r,
                                 const struct commit *commit,
                                 char **commit_encoding,
@@ -195,7 +196,7 @@ const char *repo_logmsg_reencode(struct repository *r,
 #define logmsg_reencode(c, enc, out) repo_logmsg_reencode(the_repository, c, enc, out)
 #endif
 
-extern const char *skip_blank_lines(const char *msg);
+const char *skip_blank_lines(const char *msg);
 
 /** Removes the first commit from a list sorted by date, and adds all
  * of its parents.
@@ -246,22 +247,22 @@ struct commit *get_fork_point(const char *refname, struct commit *commit);
 
 struct oid_array;
 struct ref;
-extern int register_shallow(struct repository *r, const struct object_id *oid);
-extern int unregister_shallow(const struct object_id *oid);
-extern int for_each_commit_graft(each_commit_graft_fn, void *);
-extern int is_repository_shallow(struct repository *r);
-extern struct commit_list *get_shallow_commits(struct object_array *heads,
-               int depth, int shallow_flag, int not_shallow_flag);
-extern struct commit_list *get_shallow_commits_by_rev_list(
+int register_shallow(struct repository *r, const struct object_id *oid);
+int unregister_shallow(const struct object_id *oid);
+int for_each_commit_graft(each_commit_graft_fn, void *);
+int is_repository_shallow(struct repository *r);
+struct commit_list *get_shallow_commits(struct object_array *heads,
+                                       int depth, int shallow_flag, int not_shallow_flag);
+struct commit_list *get_shallow_commits_by_rev_list(
                int ac, const char **av, int shallow_flag, int not_shallow_flag);
-extern void set_alternate_shallow_file(struct repository *r, const char *path, int override);
-extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
-                                const struct oid_array *extra);
-extern void setup_alternate_shallow(struct lock_file *shallow_lock,
-                                   const char **alternate_shallow_file,
-                                   const struct oid_array *extra);
-extern const char *setup_temporary_shallow(const struct oid_array *extra);
-extern void advertise_shallow_grafts(int);
+void set_alternate_shallow_file(struct repository *r, const char *path, int override);
+int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
+                         const struct oid_array *extra);
+void setup_alternate_shallow(struct lock_file *shallow_lock,
+                            const char **alternate_shallow_file,
+                            const struct oid_array *extra);
+const char *setup_temporary_shallow(const struct oid_array *extra);
+void advertise_shallow_grafts(int);
 
 /*
  * Initialize with prepare_shallow_info() or zero-initialize (equivalent to
@@ -282,21 +283,21 @@ struct shallow_info {
        int nr_commits;
 };
 
-extern void prepare_shallow_info(struct shallow_info *, struct oid_array *);
-extern void clear_shallow_info(struct shallow_info *);
-extern void remove_nonexistent_theirs_shallow(struct shallow_info *);
-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);
+void prepare_shallow_info(struct shallow_info *, struct oid_array *);
+void clear_shallow_info(struct shallow_info *);
+void remove_nonexistent_theirs_shallow(struct shallow_info *);
+void assign_shallow_commits_to_refs(struct shallow_info *info,
+                                   uint32_t **used,
+                                   int *ref_status);
+int delayed_reachability_test(struct shallow_info *si, int c);
 #define PRUNE_SHOW_ONLY 1
 #define PRUNE_QUICK 2
-extern void prune_shallow(unsigned options);
+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);
-extern int run_add_interactive(const char *revision, const char *patch_mode,
-                              const struct pathspec *pathspec);
+int interactive_add(int argc, const char **argv, const char *prefix, int patch);
+int run_add_interactive(const char *revision, const char *patch_mode,
+                       const struct pathspec *pathspec);
 
 struct commit_extra_header {
        struct commit_extra_header *next;
@@ -305,24 +306,24 @@ struct commit_extra_header {
        size_t len;
 };
 
-extern void append_merge_tag_headers(struct commit_list *parents,
-                                    struct commit_extra_header ***tail);
+void append_merge_tag_headers(struct commit_list *parents,
+                             struct commit_extra_header ***tail);
 
-extern int commit_tree(const char *msg, size_t msg_len,
-                      const struct object_id *tree,
-                      struct commit_list *parents, struct object_id *ret,
-                      const char *author, const char *sign_commit);
+int commit_tree(const char *msg, size_t msg_len,
+               const struct object_id *tree,
+               struct commit_list *parents, struct object_id *ret,
+               const char *author, const char *sign_commit);
 
-extern int commit_tree_extended(const char *msg, size_t msg_len,
-                               const struct object_id *tree,
-                               struct commit_list *parents,
-                               struct object_id *ret, const char *author,
-                               const char *sign_commit,
-                               struct commit_extra_header *);
+int commit_tree_extended(const char *msg, size_t msg_len,
+                        const struct object_id *tree,
+                        struct commit_list *parents,
+                        struct object_id *ret, const char *author,
+                        const char *sign_commit,
+                        struct commit_extra_header *);
 
-extern struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
+struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
 
-extern void free_commit_extra_headers(struct commit_extra_header *extra);
+void free_commit_extra_headers(struct commit_extra_header *extra);
 
 /*
  * Search the commit object contents given by "msg" for the header "key".
@@ -332,24 +333,24 @@ extern void free_commit_extra_headers(struct commit_extra_header *extra);
  * Note that some headers (like mergetag) may be multi-line. It is the caller's
  * responsibility to parse further in this case!
  */
-extern const char *find_commit_header(const char *msg, const char *key,
-                                     size_t *out_len);
+const char *find_commit_header(const char *msg, const char *key,
+                              size_t *out_len);
 
 /* Find the end of the log message, the right place for a new trailer. */
-extern size_t ignore_non_trailer(const char *buf, size_t len);
+size_t ignore_non_trailer(const char *buf, size_t len);
 
 typedef int (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
-                                void *cb_data);
+                               void *cb_data);
 
-extern int for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data);
+int for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data);
 
 struct merge_remote_desc {
        struct object *obj; /* the named object, could be a tag */
        char name[FLEX_ARRAY];
 };
-extern struct merge_remote_desc *merge_remote_util(struct commit *);
-extern void set_merge_remote_desc(struct commit *commit,
-                                 const char *name, struct object *obj);
+struct merge_remote_desc *merge_remote_util(struct commit *);
+void set_merge_remote_desc(struct commit *commit,
+                          const char *name, struct object *obj);
 
 /*
  * Given "name" from the command line to merge, find the commit object
@@ -358,9 +359,9 @@ extern void set_merge_remote_desc(struct commit *commit,
  */
 struct commit *get_merge_parent(const char *name);
 
-extern int parse_signed_commit(const struct commit *commit,
-                              struct strbuf *message, struct strbuf *signature);
-extern int remove_signature(struct strbuf *buf);
+int parse_signed_commit(const struct commit *commit,
+                       struct strbuf *message, struct strbuf *signature);
+int remove_signature(struct strbuf *buf);
 
 /*
  * Check the signature of the given commit. The result of the check is stored
@@ -369,7 +370,7 @@ extern int remove_signature(struct strbuf *buf);
  * at all.  This may allocate memory for sig->gpg_output, sig->gpg_status,
  * sig->signer and sig->key.
  */
-extern int check_commit_signature(const struct commit *commit, struct signature_check *sigc);
+int check_commit_signature(const struct commit *commit, struct signature_check *sigc);
 
 /* record author-date for each commit object */
 struct author_date_slab;
@@ -389,6 +390,6 @@ int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
 int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused);
 
 LAST_ARG_MUST_BE_NULL
-extern int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...);
+int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...);
 
 #endif /* COMMIT_H */
index d484aec20979ac9a1ea6d49b166f51436a24dfad..582a7b18869fb80c94b3a0057e4cb9ced4c6f1c2 100644 (file)
@@ -27,6 +27,8 @@ int main(int argc, const char **argv)
 {
        int result;
 
+       trace2_initialize_clock();
+
        /*
         * Always open file descriptors 0/1/2 to avoid clobbering files
         * in die().  It also avoids messing up when the pipes are dup'ed
@@ -35,11 +37,11 @@ int main(int argc, const char **argv)
        sanitize_stdfds();
        restore_sigpipe_to_default();
 
+       git_resolve_executable_dir(argv[0]);
+
        trace2_initialize();
        trace2_cmd_start(argv);
-       trace2_collect_process_info();
-
-       git_resolve_executable_dir(argv[0]);
+       trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
 
        git_setup_gettext();
 
diff --git a/compat/access.c b/compat/access.c
new file mode 100644 (file)
index 0000000..19fda3e
--- /dev/null
@@ -0,0 +1,31 @@
+#define COMPAT_CODE_ACCESS
+#include "../git-compat-util.h"
+
+/* Do the same thing access(2) does, but use the effective uid,
+ * and don't make the mistake of telling root that any file is
+ * executable.  This version uses stat(2).
+ */
+int git_access(const char *path, int mode)
+{
+       struct stat st;
+
+       /* do not interfere a normal user */
+       if (geteuid())
+               return access(path, mode);
+
+       if (stat(path, &st) < 0)
+               return -1;
+
+       /* Root can read or write any file. */
+       if (!(mode & X_OK))
+               return 0;
+
+       /* Root can execute any file that has any one of the execute
+        * bits set.
+        */
+       if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
+               return 0;
+
+       errno = EACCES;
+       return -1;
+}
index 7b105f4cd71a4ea4c1ae9f537ef0fe3eea7d9dc3..8e80ef335d84ecf5ccec6c5994e684c3951cca17 100644 (file)
@@ -1,4 +1,4 @@
-#define COMPAT_CODE
+#define COMPAT_CODE_FILENO
 #include "../git-compat-util.h"
 
 int git_fileno(FILE *stream)
index 6b04514cdc62167da027b48bae816e72811f1584..a2f74aca6a156a203ba6dd7b435622060b6870df 100644 (file)
@@ -2569,6 +2569,8 @@ void mingw_startup(void)
        wchar_t **wenv, **wargv;
        _startupinfo si;
 
+       trace2_initialize_clock();
+
        maybe_redirect_std_handles();
 
        /* get wide char arguments and environment */
index 52bd62034ba3fd47f6832961cb7d768af45212cf..8ccbd1c2c6f82d68e0c39f8ec77aa2bd4a899f2e 100644 (file)
@@ -1,5 +1,6 @@
 #include "../../cache.h"
 #include "../../json-writer.h"
+#include "lazyload.h"
 #include <Psapi.h>
 #include <tlHelp32.h>
 
@@ -137,11 +138,54 @@ static void get_is_being_debugged(void)
                                   "windows/debugger_present", 1);
 }
 
-void trace2_collect_process_info(void)
+/*
+ * Emit JSON data with the peak memory usage of the current process.
+ */
+static void get_peak_memory_info(void)
+{
+       DECLARE_PROC_ADDR(psapi.dll, BOOL, GetProcessMemoryInfo, HANDLE,
+                         PPROCESS_MEMORY_COUNTERS, DWORD);
+
+       if (INIT_PROC_ADDR(GetProcessMemoryInfo)) {
+               PROCESS_MEMORY_COUNTERS pmc;
+
+               if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc,
+                                        sizeof(pmc))) {
+                       struct json_writer jw = JSON_WRITER_INIT;
+
+                       jw_object_begin(&jw, 0);
+
+#define KV(kv) #kv, (intmax_t)pmc.kv
+
+                       jw_object_intmax(&jw, KV(PageFaultCount));
+                       jw_object_intmax(&jw, KV(PeakWorkingSetSize));
+                       jw_object_intmax(&jw, KV(PeakPagefileUsage));
+
+                       jw_end(&jw);
+
+                       trace2_data_json("process", the_repository,
+                                        "windows/memory", &jw);
+                       jw_release(&jw);
+               }
+       }
+}
+
+void trace2_collect_process_info(enum trace2_process_info_reason reason)
 {
        if (!trace2_is_enabled())
                return;
 
-       get_is_being_debugged();
-       get_ancestry();
+       switch (reason) {
+       case TRACE2_PROCESS_INFO_STARTUP:
+               get_is_being_debugged();
+               get_ancestry();
+               return;
+
+       case TRACE2_PROCESS_INFO_EXIT:
+               get_peak_memory_info();
+               return;
+
+       default:
+               BUG("trace2_collect_process_info: unknown reason '%d'", reason);
+       }
 }
index c2846df3f1d3be68b376ea154e2607726f32d01c..296a6d9cc4110bd7fcef542ac9cc9cfe04d4f4d4 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1676,7 +1676,9 @@ static int do_git_config_sequence(const struct config_options *opts,
                repo_config = NULL;
 
        current_parsing_scope = CONFIG_SCOPE_SYSTEM;
-       if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
+       if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK,
+                                                 opts->system_gently ?
+                                                 ACCESS_EACCES_OK : 0))
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
 
@@ -1688,14 +1690,15 @@ static int do_git_config_sequence(const struct config_options *opts,
                ret += git_config_from_file(fn, user_config, data);
 
        current_parsing_scope = CONFIG_SCOPE_REPO;
-       if (repo_config && !access_or_die(repo_config, R_OK, 0))
+       if (!opts->ignore_repo && repo_config &&
+           !access_or_die(repo_config, R_OK, 0))
                ret += git_config_from_file(fn, repo_config, data);
 
        /*
         * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.
         * But let's not complicate things before it's actually needed.
         */
-       if (repository_format_worktree_config) {
+       if (!opts->ignore_worktree && repository_format_worktree_config) {
                char *path = git_pathdup("config.worktree");
                if (!access_or_die(path, R_OK, 0))
                        ret += git_config_from_file(fn, path, data);
@@ -1703,7 +1706,7 @@ static int do_git_config_sequence(const struct config_options *opts,
        }
 
        current_parsing_scope = CONFIG_SCOPE_CMDLINE;
-       if (git_config_from_parameters(fn, data) < 0)
+       if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
                die(_("unable to parse command-line config"));
 
        current_parsing_scope = CONFIG_SCOPE_UNKNOWN;
@@ -1794,6 +1797,23 @@ void read_early_config(config_fn_t cb, void *data)
        strbuf_release(&gitdir);
 }
 
+/*
+ * Read config but only enumerate system and global settings.
+ * Omit any repo-local, worktree-local, or command-line settings.
+ */
+void read_very_early_config(config_fn_t cb, void *data)
+{
+       struct config_options opts = { 0 };
+
+       opts.respect_includes = 1;
+       opts.ignore_repo = 1;
+       opts.ignore_worktree = 1;
+       opts.ignore_cmdline = 1;
+       opts.system_gently = 1;
+
+       config_with_options(cb, data, NULL, &opts);
+}
+
 static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
 {
        struct config_set_element k;
@@ -2011,7 +2031,7 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
 /* Functions use to read configuration from a repository */
 static void repo_read_config(struct repository *repo)
 {
-       struct config_options opts;
+       struct config_options opts = { 0 };
 
        opts.respect_includes = 1;
        opts.commondir = repo->commondir;
index ee5d3fa7b4264f24e8b36c2e1154546ae626c191..f0ed464004da601a26d574b1cdc158abb50ae2bc 100644 (file)
--- a/config.h
+++ b/config.h
@@ -55,6 +55,10 @@ typedef int (*config_parser_event_fn_t)(enum config_event_t type,
 
 struct config_options {
        unsigned int respect_includes : 1;
+       unsigned int ignore_repo : 1;
+       unsigned int ignore_worktree : 1;
+       unsigned int ignore_cmdline : 1;
+       unsigned int system_gently : 1;
        const char *commondir;
        const char *git_dir;
        config_parser_event_fn_t event_fn;
@@ -68,62 +72,63 @@ struct config_options {
 };
 
 typedef int (*config_fn_t)(const char *, const char *, void *);
-extern int git_default_config(const char *, const char *, void *);
-extern int git_config_from_file(config_fn_t fn, const char *, void *);
-extern int git_config_from_file_with_options(config_fn_t fn, const char *,
-                                            void *,
-                                            const struct config_options *);
-extern int git_config_from_mem(config_fn_t fn,
-                              const enum config_origin_type,
-                              const char *name,
-                              const char *buf, size_t len,
-                              void *data, const struct config_options *opts);
-extern int git_config_from_blob_oid(config_fn_t fn, const char *name,
-                                   const struct object_id *oid, void *data);
-extern void git_config_push_parameter(const char *text);
-extern int git_config_from_parameters(config_fn_t fn, void *data);
-extern void read_early_config(config_fn_t cb, void *data);
-extern void git_config(config_fn_t fn, void *);
-extern int config_with_options(config_fn_t fn, void *,
-                              struct git_config_source *config_source,
-                              const struct config_options *opts);
-extern int git_parse_ssize_t(const char *, ssize_t *);
-extern int git_parse_ulong(const char *, unsigned long *);
-extern int git_parse_maybe_bool(const char *);
-extern int git_config_int(const char *, const char *);
-extern int64_t git_config_int64(const char *, const char *);
-extern unsigned long git_config_ulong(const char *, const char *);
-extern ssize_t git_config_ssize_t(const char *, const char *);
-extern int git_config_bool_or_int(const char *, const char *, int *);
-extern int git_config_bool(const char *, const char *);
-extern int git_config_string(const char **, const char *, const char *);
-extern int git_config_pathname(const char **, const char *, const char *);
-extern int git_config_expiry_date(timestamp_t *, const char *, const char *);
-extern int git_config_color(char *, const char *, const char *);
-extern int git_config_set_in_file_gently(const char *, const char *, const char *);
-extern void git_config_set_in_file(const char *, const char *, const char *);
-extern int git_config_set_gently(const char *, const char *);
-extern void git_config_set(const char *, const char *);
-extern int git_config_parse_key(const char *, char **, int *);
-extern int git_config_key_is_valid(const char *key);
-extern int git_config_set_multivar_gently(const char *, const char *, const char *, int);
-extern void git_config_set_multivar(const char *, const char *, const char *, int);
-extern int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);
-extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
-extern int git_config_rename_section(const char *, const char *);
-extern int git_config_rename_section_in_file(const char *, const char *, const char *);
-extern int git_config_copy_section(const char *, const char *);
-extern int git_config_copy_section_in_file(const char *, const char *, const char *);
-extern const char *git_etc_gitconfig(void);
-extern int git_env_bool(const char *, int);
-extern unsigned long git_env_ulong(const char *, unsigned long);
-extern int git_config_system(void);
-extern int config_error_nonbool(const char *);
+int git_default_config(const char *, const char *, void *);
+int git_config_from_file(config_fn_t fn, const char *, void *);
+int git_config_from_file_with_options(config_fn_t fn, const char *,
+                                     void *,
+                                     const struct config_options *);
+int git_config_from_mem(config_fn_t fn,
+                       const enum config_origin_type,
+                       const char *name,
+                       const char *buf, size_t len,
+                       void *data, const struct config_options *opts);
+int git_config_from_blob_oid(config_fn_t fn, const char *name,
+                            const struct object_id *oid, void *data);
+void git_config_push_parameter(const char *text);
+int git_config_from_parameters(config_fn_t fn, void *data);
+void read_early_config(config_fn_t cb, void *data);
+void read_very_early_config(config_fn_t cb, void *data);
+void git_config(config_fn_t fn, void *);
+int config_with_options(config_fn_t fn, void *,
+                       struct git_config_source *config_source,
+                       const struct config_options *opts);
+int git_parse_ssize_t(const char *, ssize_t *);
+int git_parse_ulong(const char *, unsigned long *);
+int git_parse_maybe_bool(const char *);
+int git_config_int(const char *, const char *);
+int64_t git_config_int64(const char *, const char *);
+unsigned long git_config_ulong(const char *, const char *);
+ssize_t git_config_ssize_t(const char *, const char *);
+int git_config_bool_or_int(const char *, const char *, int *);
+int git_config_bool(const char *, const char *);
+int git_config_string(const char **, const char *, const char *);
+int git_config_pathname(const char **, const char *, const char *);
+int git_config_expiry_date(timestamp_t *, const char *, const char *);
+int git_config_color(char *, const char *, const char *);
+int git_config_set_in_file_gently(const char *, const char *, const char *);
+void git_config_set_in_file(const char *, const char *, const char *);
+int git_config_set_gently(const char *, const char *);
+void git_config_set(const char *, const char *);
+int git_config_parse_key(const char *, char **, int *);
+int git_config_key_is_valid(const char *key);
+int git_config_set_multivar_gently(const char *, const char *, const char *, int);
+void git_config_set_multivar(const char *, const char *, const char *, int);
+int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);
+void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
+int git_config_rename_section(const char *, const char *);
+int git_config_rename_section_in_file(const char *, const char *, const char *);
+int git_config_copy_section(const char *, const char *);
+int git_config_copy_section_in_file(const char *, const char *, const char *);
+const char *git_etc_gitconfig(void);
+int git_env_bool(const char *, int);
+unsigned long git_env_ulong(const char *, unsigned long);
+int git_config_system(void);
+int config_error_nonbool(const char *);
 #if defined(__GNUC__)
 #define config_error_nonbool(s) (config_error_nonbool(s), const_error())
 #endif
 
-extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
+int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 
 enum config_scope {
        CONFIG_SCOPE_UNKNOWN = 0,
@@ -133,9 +138,9 @@ enum config_scope {
        CONFIG_SCOPE_CMDLINE,
 };
 
-extern enum config_scope current_config_scope(void);
-extern const char *current_config_origin_type(void);
-extern const char *current_config_name(void);
+enum config_scope current_config_scope(void);
+const char *current_config_origin_type(void);
+const char *current_config_name(void);
 
 struct config_include_data {
        int depth;
@@ -144,7 +149,7 @@ struct config_include_data {
        const struct config_options *opts;
 };
 #define CONFIG_INCLUDE_INIT { 0 }
-extern int git_config_include(const char *name, const char *value, void *data);
+int git_config_include(const char *name, const char *value, void *data);
 
 /*
  * Match and parse a config key of the form:
@@ -159,10 +164,10 @@ extern int git_config_include(const char *name, const char *value, void *data);
  * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if
  * there is no subsection at all.
  */
-extern int parse_config_key(const char *var,
-                           const char *section,
-                           const char **subsection, int *subsection_len,
-                           const char **key);
+int parse_config_key(const char *var,
+                    const char *section,
+                    const char **subsection, int *subsection_len,
+                    const char **key);
 
 struct config_set_element {
        struct hashmap_entry ent;
@@ -192,71 +197,71 @@ struct config_set {
        struct configset_list list;
 };
 
-extern void git_configset_init(struct config_set *cs);
-extern int git_configset_add_file(struct config_set *cs, const char *filename);
-extern const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
-extern void git_configset_clear(struct config_set *cs);
+void git_configset_init(struct config_set *cs);
+int git_configset_add_file(struct config_set *cs, const char *filename);
+const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
+void git_configset_clear(struct config_set *cs);
 
 /*
  * These functions return 1 if not found, and 0 if found, leaving the found
  * value in the 'dest' pointer.
  */
-extern int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
-extern int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest);
-extern int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
-extern int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
-extern int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
-extern int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
-extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
-extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
-extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
+int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
+int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
+int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
+int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
+int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
+int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
 
 /* Functions for reading a repository's config */
 struct repository;
-extern void repo_config(struct repository *repo, config_fn_t fn, void *data);
-extern int repo_config_get_value(struct repository *repo,
-                                const char *key, const char **value);
-extern const struct string_list *repo_config_get_value_multi(struct repository *repo,
-                                                            const char *key);
-extern int repo_config_get_string_const(struct repository *repo,
-                                       const char *key, const char **dest);
-extern int repo_config_get_string(struct repository *repo,
-                                 const char *key, char **dest);
-extern int repo_config_get_int(struct repository *repo,
+void repo_config(struct repository *repo, config_fn_t fn, void *data);
+int repo_config_get_value(struct repository *repo,
+                         const char *key, const char **value);
+const struct string_list *repo_config_get_value_multi(struct repository *repo,
+                                                     const char *key);
+int repo_config_get_string_const(struct repository *repo,
+                                const char *key, const char **dest);
+int repo_config_get_string(struct repository *repo,
+                          const char *key, char **dest);
+int repo_config_get_int(struct repository *repo,
+                       const char *key, int *dest);
+int repo_config_get_ulong(struct repository *repo,
+                         const char *key, unsigned long *dest);
+int repo_config_get_bool(struct repository *repo,
+                        const char *key, int *dest);
+int repo_config_get_bool_or_int(struct repository *repo,
+                               const char *key, int *is_bool, int *dest);
+int repo_config_get_maybe_bool(struct repository *repo,
                               const char *key, int *dest);
-extern int repo_config_get_ulong(struct repository *repo,
-                                const char *key, unsigned long *dest);
-extern int repo_config_get_bool(struct repository *repo,
-                               const char *key, int *dest);
-extern int repo_config_get_bool_or_int(struct repository *repo,
-                                      const char *key, int *is_bool, int *dest);
-extern int repo_config_get_maybe_bool(struct repository *repo,
-                                     const char *key, int *dest);
-extern int repo_config_get_pathname(struct repository *repo,
-                                   const char *key, const char **dest);
+int repo_config_get_pathname(struct repository *repo,
+                            const char *key, const char **dest);
 
-extern int git_config_get_value(const char *key, const char **value);
-extern const struct string_list *git_config_get_value_multi(const char *key);
-extern void git_config_clear(void);
-extern int git_config_get_string_const(const char *key, const char **dest);
-extern int git_config_get_string(const char *key, char **dest);
-extern int git_config_get_int(const char *key, int *dest);
-extern int git_config_get_ulong(const char *key, unsigned long *dest);
-extern int git_config_get_bool(const char *key, int *dest);
-extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
-extern int git_config_get_maybe_bool(const char *key, int *dest);
-extern int git_config_get_pathname(const char *key, const char **dest);
-extern int git_config_get_index_threads(int *dest);
-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);
+int git_config_get_value(const char *key, const char **value);
+const struct string_list *git_config_get_value_multi(const char *key);
+void git_config_clear(void);
+int git_config_get_string_const(const char *key, const char **dest);
+int git_config_get_string(const char *key, char **dest);
+int git_config_get_int(const char *key, int *dest);
+int git_config_get_ulong(const char *key, unsigned long *dest);
+int git_config_get_bool(const char *key, int *dest);
+int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
+int git_config_get_maybe_bool(const char *key, int *dest);
+int git_config_get_pathname(const char *key, const char **dest);
+int git_config_get_index_threads(int *dest);
+int git_config_get_untracked_cache(void);
+int git_config_get_split_index(void);
+int git_config_get_max_percent_split_change(void);
+int git_config_get_fsmonitor(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);
+int git_config_get_expiry(const char *key, const char **output);
 
 /* parse either "this many days" integer, or "5.days.ago" approxidate */
-extern int git_config_get_expiry_in_days(const char *key, timestamp_t *, timestamp_t now);
+int git_config_get_expiry_in_days(const char *key, timestamp_t *, timestamp_t now);
 
 struct key_value_info {
        const char *filename;
@@ -265,8 +270,8 @@ struct key_value_info {
        enum config_scope scope;
 };
 
-extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
-extern NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr);
+NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
+NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr);
 
 #define LOOKUP_CONFIG(mapping, var) \
        lookup_config(mapping, ARRAY_SIZE(mapping), var)
index 3605fead53a79d4e37c92a026701b89fcce501c5..b71688eeb73f10da004f3d16e3de9afd13eb9c7e 100644 (file)
@@ -271,6 +271,8 @@ ifeq ($(uname_S),AIX)
        INTERNAL_QSORT = UnfortunatelyYes
        NEEDS_LIBICONV = YesPlease
        BASIC_CFLAGS += -D_LARGE_FILES
+       FILENO_IS_A_MACRO = UnfortunatelyYes
+       NEED_ACCESS_ROOT_HANDLER = UnfortunatelyYes
        ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
                NO_PTHREADS = YesPlease
        else
@@ -394,6 +396,7 @@ ifeq ($(uname_S),Windows)
        CFLAGS =
        BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
        COMPAT_OBJS = compat/msvc.o compat/winansi.o \
+               compat/win32/path-utils.o \
                compat/win32/pthread.o compat/win32/syslog.o \
                compat/win32/trace2_win32_process_info.o \
                compat/win32/dirent.o
@@ -573,13 +576,21 @@ else
        ifneq ($(shell expr "$(uname_R)" : '1\.'),2)
                # MSys2
                prefix = /usr/
+               # Enable DEP
+               BASIC_LDFLAGS += -Wl,--nxcompat
+               # Enable ASLR (unless debugging)
+               ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
+                       BASIC_LDFLAGS += -Wl,--dynamicbase
+               endif
                ifeq (MINGW32,$(MSYSTEM))
                        prefix = /mingw32
                        HOST_CPU = i686
+                       BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
                endif
                ifeq (MINGW64,$(MSYSTEM))
                        prefix = /mingw64
                        HOST_CPU = x86_64
+                       BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
                else
                        COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
                        BASIC_LDFLAGS += -Wl,--large-address-aware
index e0d0da3c0c9bccb0b6b1b1a5a3db0097f4f10757..be3b55f1cc2b7776cdb601acdc7cffd51ae79401 100644 (file)
@@ -763,9 +763,19 @@ AC_CHECK_LIB([c], [basename],
 GIT_CONF_SUBST([NEEDS_LIBGEN])
 test -n "$NEEDS_LIBGEN" && LIBS="$LIBS -lgen"
 
-AC_CHECK_LIB([c], [gettext],
-[LIBC_CONTAINS_LIBINTL=YesPlease],
-[LIBC_CONTAINS_LIBINTL=])
+AC_DEFUN([LIBINTL_SRC], [
+AC_LANG_PROGRAM([[
+#include <libintl.h>
+]],[[
+char *msg = gettext("test");
+]])])
+
+AC_MSG_CHECKING([if libc contains libintl])
+AC_LINK_IFELSE([LIBINTL_SRC],
+       [AC_MSG_RESULT([yes])
+       LIBC_CONTAINS_LIBINTL=YesPlease],
+       [AC_MSG_RESULT([no])
+       LIBC_CONTAINS_LIBINTL=])
 GIT_CONF_SUBST([LIBC_CONTAINS_LIBINTL])
 
 #
index 32aff60979512b7f1382482435a26c51882b9f16..5f2382e01868042757901a419b5e5f34ad8bb279 100644 (file)
--- a/connect.h
+++ b/connect.h
@@ -7,19 +7,19 @@
 #define CONNECT_DIAG_URL      (1u << 1)
 #define CONNECT_IPV4          (1u << 2)
 #define CONNECT_IPV6          (1u << 3)
-extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
-extern int finish_connect(struct child_process *conn);
-extern int git_connection_is_socket(struct child_process *conn);
-extern int server_supports(const char *feature);
-extern int parse_feature_request(const char *features, const char *feature);
-extern const char *server_feature_value(const char *feature, int *len_ret);
-extern int url_is_local_not_ssh(const char *url);
+struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
+int finish_connect(struct child_process *conn);
+int git_connection_is_socket(struct child_process *conn);
+int server_supports(const char *feature);
+int parse_feature_request(const char *features, const char *feature);
+const char *server_feature_value(const char *feature, int *len_ret);
+int url_is_local_not_ssh(const char *url);
 
 struct packet_reader;
-extern enum protocol_version discover_version(struct packet_reader *reader);
+enum protocol_version discover_version(struct packet_reader *reader);
 
-extern int server_supports_v2(const char *c, int die_on_error);
-extern int server_supports_feature(const char *c, const char *feature,
-                                  int die_on_error);
+int server_supports_v2(const char *c, int die_on_error);
+int server_supports_feature(const char *c, const char *feature,
+                           int die_on_error);
 
 #endif
index 1bba888eff90a23e586807fd02b274b8e7fac097..1ab481fed69b33b48bd95c282d2c0faff897123c 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "object-store.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "connected.h"
@@ -49,6 +50,22 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
                strbuf_release(&idx_file);
        }
 
+       if (opt->check_refs_only) {
+               /*
+                * For partial clones, we don't want to have to do a regular
+                * connectivity check because we have to enumerate and exclude
+                * all promisor objects (slow), and then the connectivity check
+                * itself becomes a no-op because in a partial clone every
+                * object is a promisor object. Instead, just make sure we
+                * received the objects pointed to by each wanted ref.
+                */
+               do {
+                       if (!repo_has_object_file(the_repository, &oid))
+                               return 1;
+               } while (!fn(cb_data, &oid));
+               return 0;
+       }
+
        if (opt->shallow_file) {
                argv_array_push(&rev_list.args, "--shallow-file");
                argv_array_push(&rev_list.args, opt->shallow_file);
index 8d5a6b3ad6fe4bb0f9ca0930f8eea82543968306..ce2e7d8f2e535aac72fb317448ad20fb2133429b 100644 (file)
@@ -46,6 +46,14 @@ struct check_connected_options {
         * during a fetch.
         */
        unsigned is_deepening_fetch : 1;
+
+       /*
+        * If non-zero, only check the top-level objects referenced by the
+        * wanted refs (passed in as cb_data). This is useful for partial
+        * clones, where enumerating and excluding all promisor objects is very
+        * slow and the commit-walk itself becomes a no-op.
+        */
+       unsigned check_refs_only : 1;
 };
 
 #define CHECK_CONNECTED_INIT { 0 }
index c49aa558f0fe6b74b9d4d4a7779f0a9f32747388..d03453341e84c912e14d8b421b47857e7eb244b1 100644 (file)
@@ -10,19 +10,25 @@ expression c;
 - c->maybe_tree->object.oid.hash
 + get_commit_tree_oid(c)->hash
 
-// These excluded functions must access c->maybe_tree direcly.
 @@
-identifier f !~ "^(get_commit_tree|get_commit_tree_in_graph_one|load_tree_for_commit)$";
+identifier f !~ "^set_commit_tree$";
 expression c;
+expression s;
 @@
   f(...) {<...
-- c->maybe_tree
-+ get_commit_tree(c)
+- c->maybe_tree = s
++ set_commit_tree(c, s)
   ...>}
 
+// These excluded functions must access c->maybe_tree direcly.
+// Note that if c->maybe_tree is written somewhere outside of these
+// functions, then the recommended transformation will be bogus with
+// repo_get_commit_tree() on the LHS.
 @@
+identifier f !~ "^(repo_get_commit_tree|get_commit_tree_in_graph_one|load_tree_for_commit|set_commit_tree)$";
 expression c;
-expression s;
 @@
-- get_commit_tree(c) = s
-+ c->maybe_tree = s
+  f(...) {<...
+- c->maybe_tree
++ repo_get_commit_tree(specify_the_right_repo_here, c)
+  ...>}
index 536754583b59945e984ad487b6e524e5d1de7056..7440aa1c4638103c952c225f2c9b77d8c9451b08 100644 (file)
@@ -4,6 +4,11 @@ package DiffHighlight;
 use warnings FATAL => 'all';
 use strict;
 
+# Use the correct value for both UNIX and Windows (/dev/null vs nul)
+use File::Spec;
+
+my $NULL = File::Spec->devnull();
+
 # Highlight by reversing foreground and background. You could do
 # other things like bold or underline if you prefer.
 my @OLD_HIGHLIGHT = (
@@ -134,7 +139,7 @@ sub highlight_stdin {
 # fallback, which means we will work even if git can't be run.
 sub color_config {
        my ($key, $default) = @_;
-       my $s = `git config --get-color $key 2>/dev/null`;
+       my $s = `git config --get-color $key 2>$NULL`;
        return length($s) ? $s : $default;
 }
 
index 3bf7184736365c2034b6851ca4a1f3c9d052cb42..a98b1eee53f40367ad06c7aa82da7af0ca6ed6bc 100644 (file)
@@ -25,22 +25,22 @@ struct hashfile_checkpoint {
        git_hash_ctx ctx;
 };
 
-extern void hashfile_checkpoint(struct hashfile *, struct hashfile_checkpoint *);
-extern int hashfile_truncate(struct hashfile *, struct hashfile_checkpoint *);
+void hashfile_checkpoint(struct hashfile *, struct hashfile_checkpoint *);
+int hashfile_truncate(struct hashfile *, struct hashfile_checkpoint *);
 
 /* finalize_hashfile flags */
 #define CSUM_CLOSE             1
 #define CSUM_FSYNC             2
 #define CSUM_HASH_IN_STREAM    4
 
-extern struct hashfile *hashfd(int fd, const char *name);
-extern struct hashfile *hashfd_check(const char *name);
-extern struct hashfile *hashfd_throughput(int fd, const char *name, struct progress *tp);
-extern int finalize_hashfile(struct hashfile *, unsigned char *, unsigned int);
-extern void hashwrite(struct hashfile *, const void *, unsigned int);
-extern void hashflush(struct hashfile *f);
-extern void crc32_begin(struct hashfile *);
-extern uint32_t crc32_end(struct hashfile *);
+struct hashfile *hashfd(int fd, const char *name);
+struct hashfile *hashfd_check(const char *name);
+struct hashfile *hashfd_throughput(int fd, const char *name, struct progress *tp);
+int finalize_hashfile(struct hashfile *, unsigned char *, unsigned int);
+void hashwrite(struct hashfile *, const void *, unsigned int);
+void hashflush(struct hashfile *f);
+void crc32_begin(struct hashfile *);
+uint32_t crc32_end(struct hashfile *);
 
 static inline void hashwrite_u8(struct hashfile *f, uint8_t data)
 {
index 9014c1e996c60d9b3e1bd27ffa01b9c2d0c8ae80..ee43dee1f008882094ca9c85f6b28b86ec88fdf6 100644 (file)
@@ -50,12 +50,12 @@ struct decoration {
  * NULL), returning the previously associated pointer. If there is no previous
  * association, this function returns NULL.
  */
-extern void *add_decoration(struct decoration *n, const struct object *obj, void *decoration);
+void *add_decoration(struct decoration *n, const struct object *obj, void *decoration);
 
 /*
  * Return the pointer associated to the given object. If there is no
  * association, this function returns NULL.
  */
-extern void *lookup_decoration(struct decoration *n, const struct object *obj);
+void *lookup_decoration(struct decoration *n, const struct object *obj);
 
 #endif
diff --git a/delta.h b/delta.h
index 9b67531dfa4e6511d0c7a4d6d400a3d3719603bc..2df5fe13d954dfad4c6b1e80be7b45b281d7ef6d 100644 (file)
--- a/delta.h
+++ b/delta.h
@@ -13,7 +13,7 @@ struct delta_index;
  * before free_delta_index() is called.  The returned pointer must be freed
  * using free_delta_index().
  */
-extern struct delta_index *
+struct delta_index *
 create_delta_index(const void *buf, unsigned long bufsize);
 
 /*
@@ -21,14 +21,14 @@ create_delta_index(const void *buf, unsigned long bufsize);
  *
  * Given pointer must be what create_delta_index() returned, or NULL.
  */
-extern void free_delta_index(struct delta_index *index);
+void free_delta_index(struct delta_index *index);
 
 /*
  * sizeof_delta_index: returns memory usage of delta index
  *
  * Given pointer must be what create_delta_index() returned, or NULL.
  */
-extern unsigned long sizeof_delta_index(struct delta_index *index);
+unsigned long sizeof_delta_index(struct delta_index *index);
 
 /*
  * create_delta: create a delta from given index for the given buffer
@@ -40,7 +40,7 @@ extern unsigned long sizeof_delta_index(struct delta_index *index);
  * returned and *delta_size is updated with its size.  The returned buffer
  * must be freed by the caller.
  */
-extern void *
+void *
 create_delta(const struct delta_index *index,
             const void *buf, unsigned long bufsize,
             unsigned long *delta_size, unsigned long max_delta_size);
@@ -75,9 +75,9 @@ diff_delta(const void *src_buf, unsigned long src_bufsize,
  * *trg_bufsize is updated with its size.  On failure a NULL pointer is
  * returned.  The returned buffer must be freed by the caller.
  */
-extern void *patch_delta(const void *src_buf, unsigned long src_size,
-                        const void *delta_buf, unsigned long delta_size,
-                        unsigned long *dst_size);
+void *patch_delta(const void *src_buf, unsigned long src_size,
+                 const void *delta_buf, unsigned long delta_size,
+                 unsigned long *dst_size);
 
 /* the smallest possible delta size is 4 bytes */
 #define DELTA_SIZE_MIN 4
diff --git a/dir.c b/dir.c
index ad30a5a46dd8d902874d3c3185f905c13a18ba7f..ba4a51c296efcad9861ebfb4b318fbd5cb3025a4 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1466,9 +1466,11 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
                        return path_none;
                }
                if (!(dir->flags & DIR_NO_GITLINKS)) {
-                       struct object_id oid;
-                       if (resolve_gitlink_ref(dirname, "HEAD", &oid) == 0)
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addstr(&sb, dirname);
+                       if (is_nonbare_repository_dir(&sb))
                                return exclude ? path_excluded : path_untracked;
+                       strbuf_release(&sb);
                }
                return path_recurse;
        }
@@ -2314,6 +2316,14 @@ int file_exists(const char *f)
        return lstat(f, &sb) == 0;
 }
 
+int repo_file_exists(struct repository *repo, const char *path)
+{
+       if (repo != the_repository)
+               BUG("do not know how to check file existence in arbitrary repo");
+
+       return file_exists(path);
+}
+
 static int cmp_icase(char a, char b)
 {
        if (a == b)
@@ -2728,54 +2738,49 @@ static int read_one_dir(struct untracked_cache_dir **untracked_,
                        struct read_data *rd)
 {
        struct untracked_cache_dir ud, *untracked;
-       const unsigned char *next, *data = rd->data, *end = rd->end;
+       const unsigned char *data = rd->data, *end = rd->end;
+       const unsigned char *eos;
        unsigned int value;
-       int i, len;
+       int i;
 
        memset(&ud, 0, sizeof(ud));
 
-       next = data;
-       value = decode_varint(&next);
-       if (next > end)
+       value = decode_varint(&data);
+       if (data > end)
                return -1;
        ud.recurse         = 1;
        ud.untracked_alloc = value;
        ud.untracked_nr    = value;
        if (ud.untracked_nr)
                ALLOC_ARRAY(ud.untracked, ud.untracked_nr);
-       data = next;
 
-       next = data;
-       ud.dirs_alloc = ud.dirs_nr = decode_varint(&next);
-       if (next > end)
+       ud.dirs_alloc = ud.dirs_nr = decode_varint(&data);
+       if (data > end)
                return -1;
        ALLOC_ARRAY(ud.dirs, ud.dirs_nr);
-       data = next;
 
-       len = strlen((const char *)data);
-       next = data + len + 1;
-       if (next > rd->end)
+       eos = memchr(data, '\0', end - data);
+       if (!eos || eos == end)
                return -1;
-       *untracked_ = untracked = xmalloc(st_add3(sizeof(*untracked), len, 1));
+
+       *untracked_ = untracked = xmalloc(st_add3(sizeof(*untracked), eos - data, 1));
        memcpy(untracked, &ud, sizeof(ud));
-       memcpy(untracked->name, data, len + 1);
-       data = next;
+       memcpy(untracked->name, data, eos - data + 1);
+       data = eos + 1;
 
        for (i = 0; i < untracked->untracked_nr; i++) {
-               len = strlen((const char *)data);
-               next = data + len + 1;
-               if (next > rd->end)
+               eos = memchr(data, '\0', end - data);
+               if (!eos || eos == end)
                        return -1;
-               untracked->untracked[i] = xstrdup((const char*)data);
-               data = next;
+               untracked->untracked[i] = xmemdupz(data, eos - data);
+               data = eos + 1;
        }
 
        rd->ucd[rd->index++] = untracked;
        rd->data = data;
 
        for (i = 0; i < untracked->dirs_nr; i++) {
-               len = read_one_dir(untracked->dirs + i, rd);
-               if (len < 0)
+               if (read_one_dir(untracked->dirs + i, rd) < 0)
                        return -1;
        }
        return 0;
diff --git a/dir.h b/dir.h
index 823bae628bc8db03dee6ef0c5601b05a6ea942f5..680079bbe3241ff84fc8260009515f03b6ba819e 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -202,7 +202,7 @@ struct dir_struct {
 };
 
 /*Count the number of slashes for string s*/
-extern int count_slashes(const char *s);
+int count_slashes(const char *s);
 
 /*
  * The ordering of these constants is significant, with
@@ -213,27 +213,27 @@ extern int count_slashes(const char *s);
 #define MATCHED_RECURSIVELY 1
 #define MATCHED_FNMATCH 2
 #define MATCHED_EXACTLY 3
-extern int simple_length(const char *match);
-extern int no_wildcard(const char *string);
-extern char *common_prefix(const struct pathspec *pathspec);
-extern int match_pathspec(const struct index_state *istate,
-                         const struct pathspec *pathspec,
-                         const char *name, int namelen,
-                         int prefix, char *seen, int is_dir);
-extern int report_path_error(const char *ps_matched, const struct pathspec *pathspec);
-extern int within_depth(const char *name, int namelen, int depth, int max_depth);
-
-extern int fill_directory(struct dir_struct *dir,
-                         struct index_state *istate,
-                         const struct pathspec *pathspec);
-extern int read_directory(struct dir_struct *, struct index_state *istate,
-                         const char *path, int len,
-                         const struct pathspec *pathspec);
-
-extern int is_excluded_from_list(const char *pathname, int pathlen,
-                                const char *basename, int *dtype,
-                                struct exclude_list *el,
-                                struct index_state *istate);
+int simple_length(const char *match);
+int no_wildcard(const char *string);
+char *common_prefix(const struct pathspec *pathspec);
+int match_pathspec(const struct index_state *istate,
+                  const struct pathspec *pathspec,
+                  const char *name, int namelen,
+                  int prefix, char *seen, int is_dir);
+int report_path_error(const char *ps_matched, const struct pathspec *pathspec);
+int within_depth(const char *name, int namelen, int depth, int max_depth);
+
+int fill_directory(struct dir_struct *dir,
+                  struct index_state *istate,
+                  const struct pathspec *pathspec);
+int read_directory(struct dir_struct *, struct index_state *istate,
+                  const char *path, int len,
+                  const struct pathspec *pathspec);
+
+int is_excluded_from_list(const char *pathname, int pathlen,
+                         const char *basename, int *dtype,
+                         struct exclude_list *el,
+                         struct index_state *istate);
 struct dir_entry *dir_add_ignored(struct dir_struct *dir,
                                  struct index_state *istate,
                                  const char *pathname, int len);
@@ -242,37 +242,39 @@ struct dir_entry *dir_add_ignored(struct dir_struct *dir,
  * these implement the matching logic for dir.c:excluded_from_list and
  * attr.c:path_matches()
  */
-extern int match_basename(const char *, int,
-                         const char *, int, int, unsigned);
-extern int match_pathname(const char *, int,
-                         const char *, int,
-                         const char *, int, int, unsigned);
-
-extern struct exclude *last_exclude_matching(struct dir_struct *dir,
-                                            struct index_state *istate,
-                                            const char *name, int *dtype);
-
-extern int is_excluded(struct dir_struct *dir,
-                      struct index_state *istate,
-                      const char *name, int *dtype);
-
-extern struct exclude_list *add_exclude_list(struct dir_struct *dir,
-                                            int group_type, const char *src);
-extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
-                                         struct exclude_list *el, struct  index_state *istate);
-extern void add_excludes_from_file(struct dir_struct *, const char *fname);
-extern int add_excludes_from_blob_to_list(struct object_id *oid,
-                                         const char *base, int baselen,
-                                         struct exclude_list *el);
-extern void parse_exclude_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
-extern void add_exclude(const char *string, const char *base,
-                       int baselen, struct exclude_list *el, int srcpos);
-extern void clear_exclude_list(struct exclude_list *el);
-extern void clear_directory(struct dir_struct *dir);
-extern int file_exists(const char *);
-
-extern int is_inside_dir(const char *dir);
-extern int dir_inside_of(const char *subdir, const char *dir);
+int match_basename(const char *, int,
+                  const char *, int, int, unsigned);
+int match_pathname(const char *, int,
+                  const char *, int,
+                  const char *, int, int, unsigned);
+
+struct exclude *last_exclude_matching(struct dir_struct *dir,
+                                     struct index_state *istate,
+                                     const char *name, int *dtype);
+
+int is_excluded(struct dir_struct *dir,
+               struct index_state *istate,
+               const char *name, int *dtype);
+
+struct exclude_list *add_exclude_list(struct dir_struct *dir,
+                                     int group_type, const char *src);
+int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
+                                  struct exclude_list *el, struct  index_state *istate);
+void add_excludes_from_file(struct dir_struct *, const char *fname);
+int add_excludes_from_blob_to_list(struct object_id *oid,
+                                  const char *base, int baselen,
+                                  struct exclude_list *el);
+void parse_exclude_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
+void add_exclude(const char *string, const char *base,
+                int baselen, struct exclude_list *el, int srcpos);
+void clear_exclude_list(struct exclude_list *el);
+void clear_directory(struct dir_struct *dir);
+
+int repo_file_exists(struct repository *repo, const char *path);
+int file_exists(const char *);
+
+int is_inside_dir(const char *dir);
+int dir_inside_of(const char *subdir, const char *dir);
 
 static inline int is_dot_or_dotdot(const char *name)
 {
@@ -281,9 +283,9 @@ static inline int is_dot_or_dotdot(const char *name)
                 (name[1] == '.' && name[2] == '\0')));
 }
 
-extern int is_empty_dir(const char *dir);
+int is_empty_dir(const char *dir);
 
-extern void setup_standard_excludes(struct dir_struct *dir);
+void setup_standard_excludes(struct dir_struct *dir);
 
 
 /* Constants for remove_dir_recursively: */
@@ -311,26 +313,26 @@ extern void setup_standard_excludes(struct dir_struct *dir);
  * This function uses path as temporary scratch space, but restores it
  * before returning.
  */
-extern int remove_dir_recursively(struct strbuf *path, int flag);
+int remove_dir_recursively(struct strbuf *path, int flag);
 
 /* tries to remove the path with empty directories along it, ignores ENOENT */
-extern int remove_path(const char *path);
+int remove_path(const char *path);
 
-extern int fspathcmp(const char *a, const char *b);
-extern int fspathncmp(const char *a, const char *b, size_t count);
+int fspathcmp(const char *a, const char *b);
+int fspathncmp(const char *a, const char *b, size_t count);
 
 /*
  * The prefix part of pattern must not contains wildcards.
  */
 struct pathspec_item;
-extern int git_fnmatch(const struct pathspec_item *item,
-                      const char *pattern, const char *string,
-                      int prefix);
+int git_fnmatch(const struct pathspec_item *item,
+               const char *pattern, const char *string,
+               int prefix);
 
-extern int submodule_path_match(const struct index_state *istate,
-                               const struct pathspec *ps,
-                               const char *submodule_name,
-                               char *seen);
+int submodule_path_match(const struct index_state *istate,
+                        const struct pathspec *ps,
+                        const char *submodule_name,
+                        char *seen);
 
 static inline int ce_path_match(const struct index_state *istate,
                                const struct cache_entry *ce,
@@ -372,10 +374,10 @@ void remove_untracked_cache(struct index_state *istate);
  * When `recurse_into_nested` is set, recurse into any nested submodules,
  * connecting them as well.
  */
-extern void connect_work_tree_and_git_dir(const char *work_tree,
-                                         const char *git_dir,
-                                         int recurse_into_nested);
-extern void relocate_gitdir(const char *path,
-                           const char *old_git_dir,
-                           const char *new_git_dir);
+void connect_work_tree_and_git_dir(const char *work_tree,
+                                  const char *git_dir,
+                                  int recurse_into_nested);
+void relocate_gitdir(const char *path,
+                    const char *old_git_dir,
+                    const char *new_git_dir);
 #endif
index 2522453cdaa74d9e9d806f20036518a394dd32e2..8cd1df28d3f11d165bc91fc34c90fc94802c9f61 100644 (file)
@@ -3,14 +3,14 @@
 
 struct argv_array;
 
-extern void git_set_exec_path(const char *exec_path);
-extern void git_resolve_executable_dir(const char *path);
-extern const char *git_exec_path(void);
-extern void setup_path(void);
-extern const char **prepare_git_cmd(struct argv_array *out, const char **argv);
-extern int execv_git_cmd(const char **argv); /* NULL terminated */
+void git_set_exec_path(const char *exec_path);
+void git_resolve_executable_dir(const char *path);
+const char *git_exec_path(void);
+void setup_path(void);
+const char **prepare_git_cmd(struct argv_array *out, const char **argv);
+int execv_git_cmd(const char **argv); /* NULL terminated */
 LAST_ARG_MUST_BE_NULL
-extern int execl_git_cmd(const char *cmd, ...);
-extern char *system_path(const char *path);
+int execl_git_cmd(const char *cmd, ...);
+char *system_path(const char *path);
 
 #endif /* GIT_EXEC_CMD_H */
index b28d3a6113dc671884e95cbbc3904e4b2d26d727..01e3aa88c50c1994065a99515bd13f484558b553 100644 (file)
@@ -2,6 +2,6 @@
 #define FMT_MERGE_MSG_H
 
 extern int merge_log_config;
-extern int fmt_merge_msg_config(const char *key, const char *value, void *cb);
+int fmt_merge_msg_config(const char *key, const char *value, void *cb);
 
 #endif /* FMT_MERGE_MSG_H */
diff --git a/fsck.c b/fsck.c
index 2260adb71e7a9f1091d14bb635a4f082592b9512..4703f55561452c3fca5401aba3fcec7b170f1c6a 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -604,7 +604,7 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
        o_name = NULL;
 
        while (desc.size) {
-               unsigned mode;
+               unsigned short mode;
                const char *name;
                const struct object_id *oid;
 
index 01017c43aa68be952ec6b89530fb5aa85c8a098b..8489fa3244976bb3e489c030e726cf06f0e4e9b3 100644 (file)
@@ -10,31 +10,31 @@ extern struct trace_key trace_fsmonitor;
  * Read the fsmonitor index extension and (if configured) restore the
  * CE_FSMONITOR_VALID state.
  */
-extern int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
+int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
 
 /*
  * Fill the fsmonitor_dirty ewah bits with their state from the index,
  * before it is split during writing.
  */
-extern void fill_fsmonitor_bitmap(struct index_state *istate);
+void fill_fsmonitor_bitmap(struct index_state *istate);
 
 /*
  * Write the CE_FSMONITOR_VALID state into the fsmonitor index
  * extension.  Reads from the fsmonitor_dirty ewah in the index.
  */
-extern void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
+void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
 
 /*
  * Add/remove the fsmonitor index extension
  */
-extern void add_fsmonitor(struct index_state *istate);
-extern void remove_fsmonitor(struct index_state *istate);
+void add_fsmonitor(struct index_state *istate);
+void remove_fsmonitor(struct index_state *istate);
 
 /*
  * Add/remove the fsmonitor index extension as necessary based on the current
  * core.fsmonitor setting.
  */
-extern void tweak_fsmonitor(struct index_state *istate);
+void tweak_fsmonitor(struct index_state *istate);
 
 /*
  * Run the configured fsmonitor integration script and clear the
@@ -42,7 +42,7 @@ extern void tweak_fsmonitor(struct index_state *istate);
  * any corresponding untracked cache directory structures. Optimized to only
  * run the first time it is called.
  */
-extern void refresh_fsmonitor(struct index_state *istate);
+void refresh_fsmonitor(struct index_state *istate);
 
 /*
  * Set the given cache entries CE_FSMONITOR_VALID bit. This should be
index 709d67405bba65f0b1d5468fe58a92fa41c30398..71158f7d8ba9c774ca6790ea8594e96accb17fce 100755 (executable)
@@ -6,7 +6,7 @@ die () {
 }
 
 command_list () {
-       grep -v '^#' "$1"
+       eval "grep -ve '^#' $exclude_programs" <"$1"
 }
 
 get_categories () {
@@ -93,6 +93,14 @@ EOF
 EOF
 }
 
+exclude_programs=
+while test "--exclude-program" = "$1"
+do
+       shift
+       exclude_programs="$exclude_programs -e \"^$1 \""
+       shift
+done
+
 echo "/* Automatically generated by generate-cmdlist.sh */
 struct cmdname_help {
        const char *name;
index 71255e503eeb70e691e03ac1be0e2a249244a096..bee52eb11347da9336e9f1906f7973aa0e224d3b 100644 (file)
--- a/gettext.h
+++ b/gettext.h
 
 #define FORMAT_PRESERVING(n) __attribute__((format_arg(n)))
 
-extern int use_gettext_poison(void);
+int use_gettext_poison(void);
 
 #ifndef NO_GETTEXT
-extern void git_setup_gettext(void);
-extern int gettext_width(const char *s);
+void git_setup_gettext(void);
+int gettext_width(const char *s);
 #else
 static inline void git_setup_gettext(void)
 {
@@ -87,6 +87,6 @@ const char *Q_(const char *msgid, const char *plu, unsigned long n)
 #endif
 
 const char *get_preferred_languages(void);
-extern int is_utf8_locale(void);
+int is_utf8_locale(void);
 
 #endif
index 31b47932bd5144f306a9aa1b122fdf3cb0886284..cc0e7e97334ec90a355f9c8eb088c6810a91193c 100644 (file)
 #include "compat/win32/path-utils.h"
 #include "compat/mingw.h"
 #elif defined(_MSC_VER)
+#include "compat/win32/path-utils.h"
 #include "compat/msvc.h"
 #else
 #include <sys/utsname.h>
@@ -249,7 +250,7 @@ typedef unsigned long uintptr_t;
 
 #ifdef MKDIR_WO_TRAILING_SLASH
 #define mkdir(a,b) compat_mkdir_wo_trailing_slash((a),(b))
-extern int compat_mkdir_wo_trailing_slash(const char*, mode_t);
+int compat_mkdir_wo_trailing_slash(const char*, mode_t);
 #endif
 
 #ifdef NO_STRUCT_ITIMERVAL
@@ -267,9 +268,9 @@ struct itimerval {
 #include <libgen.h>
 #else
 #define basename gitbasename
-extern char *gitbasename(char *);
+char *gitbasename(char *);
 #define dirname gitdirname
-extern char *gitdirname(char *);
+char *gitdirname(char *);
 #endif
 
 #ifndef NO_ICONV
@@ -446,15 +447,15 @@ static inline char *git_find_last_dir_sep(const char *path)
 struct strbuf;
 
 /* General helper functions */
-extern void vreportf(const char *prefix, const char *err, va_list params);
-extern NORETURN void usage(const char *err);
-extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
+void vreportf(const char *prefix, const char *err, va_list params);
+NORETURN void usage(const char *err);
+NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
+NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
+NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
+int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
+void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
 #ifndef NO_OPENSSL
 #ifdef APPLE_COMMON_CRYPTO
@@ -482,15 +483,15 @@ static inline int const_error(void)
 #define error_errno(...) (error_errno(__VA_ARGS__), const_error())
 #endif
 
-extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
-extern void set_error_routine(void (*routine)(const char *err, va_list params));
+void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
+void set_error_routine(void (*routine)(const char *err, va_list params));
 extern void (*get_error_routine(void))(const char *err, va_list params);
-extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
+void set_warn_routine(void (*routine)(const char *warn, va_list params));
 extern void (*get_warn_routine(void))(const char *warn, va_list params);
-extern void set_die_is_recursing_routine(int (*routine)(void));
+void set_die_is_recursing_routine(int (*routine)(void));
 
-extern int starts_with(const char *str, const char *prefix);
-extern int istarts_with(const char *str, const char *prefix);
+int starts_with(const char *str, const char *prefix);
+int istarts_with(const char *str, const char *prefix);
 
 /*
  * If the string "str" begins with the string found in "prefix", return 1.
@@ -613,8 +614,8 @@ static inline int ends_with(const char *str, const char *suffix)
 
 #define mmap git_mmap
 #define munmap git_munmap
-extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-extern int git_munmap(void *start, size_t length);
+void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+int git_munmap(void *start, size_t length);
 
 #else /* NO_MMAP || USE_WIN32_MMAP */
 
@@ -668,17 +669,17 @@ extern int git_munmap(void *start, size_t length);
 #undef stat
 #endif
 #define stat(path, buf) git_stat(path, buf)
-extern int git_stat(const char *, struct stat *);
+int git_stat(const char *, struct stat *);
 #ifdef fstat
 #undef fstat
 #endif
 #define fstat(fd, buf) git_fstat(fd, buf)
-extern int git_fstat(int, struct stat *);
+int git_fstat(int, struct stat *);
 #ifdef lstat
 #undef lstat
 #endif
 #define lstat(path, buf) git_lstat(path, buf)
-extern int git_lstat(const char *, struct stat *);
+int git_lstat(const char *, struct stat *);
 #endif
 
 #define DEFAULT_PACKED_GIT_LIMIT \
@@ -686,50 +687,50 @@ extern int git_lstat(const char *, struct stat *);
 
 #ifdef NO_PREAD
 #define pread git_pread
-extern ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
+ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
 #endif
 /*
  * Forward decl that will remind us if its twin in cache.h changes.
  * This function is used in compat/pread.c.  But we can't include
  * cache.h there.
  */
-extern ssize_t read_in_full(int fd, void *buf, size_t count);
+ssize_t read_in_full(int fd, void *buf, size_t count);
 
 #ifdef NO_SETENV
 #define setenv gitsetenv
-extern int gitsetenv(const char *, const char *, int);
+int gitsetenv(const char *, const char *, int);
 #endif
 
 #ifdef NO_MKDTEMP
 #define mkdtemp gitmkdtemp
-extern char *gitmkdtemp(char *);
+char *gitmkdtemp(char *);
 #endif
 
 #ifdef NO_UNSETENV
 #define unsetenv gitunsetenv
-extern void gitunsetenv(const char *);
+void gitunsetenv(const char *);
 #endif
 
 #ifdef NO_STRCASESTR
 #define strcasestr gitstrcasestr
-extern char *gitstrcasestr(const char *haystack, const char *needle);
+char *gitstrcasestr(const char *haystack, const char *needle);
 #endif
 
 #ifdef NO_STRLCPY
 #define strlcpy gitstrlcpy
-extern size_t gitstrlcpy(char *, const char *, size_t);
+size_t gitstrlcpy(char *, const char *, size_t);
 #endif
 
 #ifdef NO_STRTOUMAX
 #define strtoumax gitstrtoumax
-extern uintmax_t gitstrtoumax(const char *, char **, int);
+uintmax_t gitstrtoumax(const char *, char **, int);
 #define strtoimax gitstrtoimax
-extern intmax_t gitstrtoimax(const char *, char **, int);
+intmax_t gitstrtoimax(const char *, char **, int);
 #endif
 
 #ifdef NO_HSTRERROR
 #define hstrerror githstrerror
-extern const char *githstrerror(int herror);
+const char *githstrerror(int herror);
 #endif
 
 #ifdef NO_MEMMEM
@@ -761,7 +762,7 @@ char *gitstrdup(const char *s);
 #  endif
 #  define fopen(a,b) git_fopen(a,b)
 # endif
-extern FILE *git_fopen(const char*, const char*);
+FILE *git_fopen(const char*, const char*);
 #endif
 
 #ifdef SNPRINTF_RETURNS_BOGUS
@@ -769,14 +770,14 @@ extern FILE *git_fopen(const char*, const char*);
 #undef snprintf
 #endif
 #define snprintf git_snprintf
-extern int git_snprintf(char *str, size_t maxsize,
-                       const char *format, ...);
+int git_snprintf(char *str, size_t maxsize,
+                const char *format, ...);
 #ifdef vsnprintf
 #undef vsnprintf
 #endif
 #define vsnprintf git_vsnprintf
-extern int git_vsnprintf(char *str, size_t maxsize,
-                        const char *format, va_list ap);
+int git_vsnprintf(char *str, size_t maxsize,
+                 const char *format, va_list ap);
 #endif
 
 #ifdef __GLIBC_PREREQ
@@ -805,11 +806,11 @@ const char *inet_ntop(int af, const void *src, char *dst, size_t size);
 
 #ifdef NO_PTHREADS
 #define atexit git_atexit
-extern int git_atexit(void (*handler)(void));
+int git_atexit(void (*handler)(void));
 #endif
 
 typedef void (*try_to_free_t)(size_t);
-extern try_to_free_t set_try_to_free_routine(try_to_free_t);
+try_to_free_t set_try_to_free_routine(try_to_free_t);
 
 static inline size_t st_add(size_t a, size_t b)
 {
@@ -845,28 +846,28 @@ static inline size_t st_sub(size_t a, size_t b)
 # define xalloca(size)      (xmalloc(size))
 # define xalloca_free(p)    (free(p))
 #endif
-extern char *xstrdup(const char *str);
-extern void *xmalloc(size_t size);
-extern void *xmallocz(size_t size);
-extern void *xmallocz_gently(size_t size);
-extern void *xmemdupz(const void *data, size_t len);
-extern char *xstrndup(const char *str, size_t len);
-extern void *xrealloc(void *ptr, size_t size);
-extern void *xcalloc(size_t nmemb, size_t size);
-extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-extern void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-extern int xopen(const char *path, int flags, ...);
-extern ssize_t xread(int fd, void *buf, size_t len);
-extern ssize_t xwrite(int fd, const void *buf, size_t len);
-extern ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
-extern int xdup(int fd);
-extern FILE *xfopen(const char *path, const char *mode);
-extern FILE *xfdopen(int fd, const char *mode);
-extern int xmkstemp(char *temp_filename);
-extern int xmkstemp_mode(char *temp_filename, int mode);
-extern char *xgetcwd(void);
-extern FILE *fopen_for_writing(const char *path);
-extern FILE *fopen_or_warn(const char *path, const char *mode);
+char *xstrdup(const char *str);
+void *xmalloc(size_t size);
+void *xmallocz(size_t size);
+void *xmallocz_gently(size_t size);
+void *xmemdupz(const void *data, size_t len);
+char *xstrndup(const char *str, size_t len);
+void *xrealloc(void *ptr, size_t size);
+void *xcalloc(size_t nmemb, size_t size);
+void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+int xopen(const char *path, int flags, ...);
+ssize_t xread(int fd, void *buf, size_t len);
+ssize_t xwrite(int fd, const void *buf, size_t len);
+ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
+int xdup(int fd);
+FILE *xfopen(const char *path, const char *mode);
+FILE *xfdopen(int fd, const char *mode);
+int xmkstemp(char *temp_filename);
+int xmkstemp_mode(char *temp_filename, int mode);
+char *xgetcwd(void);
+FILE *fopen_for_writing(const char *path);
+FILE *fopen_or_warn(const char *path, const char *mode);
 
 /*
  * FREE_AND_NULL(ptr) is like free(ptr) followed by ptr = NULL. Note
@@ -966,13 +967,13 @@ static inline size_t xsize_t(off_t len)
 }
 
 __attribute__((format (printf, 3, 4)))
-extern int xsnprintf(char *dst, size_t max, const char *fmt, ...);
+int xsnprintf(char *dst, size_t max, const char *fmt, ...);
 
 #ifndef HOST_NAME_MAX
 #define HOST_NAME_MAX 256
 #endif
 
-extern int xgethostname(char *buf, size_t len);
+int xgethostname(char *buf, size_t len);
 
 /* in ctype.c, for kwset users */
 extern const unsigned char tolower_trans_tbl[256];
@@ -1236,12 +1237,22 @@ struct tm *git_gmtime_r(const time_t *, struct tm *);
 
 #ifdef FILENO_IS_A_MACRO
 int git_fileno(FILE *stream);
-# ifndef COMPAT_CODE
+# ifndef COMPAT_CODE_FILENO
 #  undef fileno
 #  define fileno(p) git_fileno(p)
 # endif
 #endif
 
+#ifdef NEED_ACCESS_ROOT_HANDLER
+int git_access(const char *path, int mode);
+# ifndef COMPAT_CODE_ACCESS
+#  ifdef access
+#  undef access
+#  endif
+#  define access(path, mode) git_access(path, mode)
+# endif
+#endif
+
 /*
  * Our code often opens a path to an optional file, to work on its
  * contents when we can successfully open it.  We can ignore a failure
@@ -1257,7 +1268,7 @@ static inline int is_missing_file_error(int errno_)
        return (errno_ == ENOENT || errno_ == ENOTDIR);
 }
 
-extern int cmd_main(int, const char **);
+int cmd_main(int, const char **);
 
 /*
  * Intercept all calls to exit() and route them to trace2 to
@@ -1280,7 +1291,7 @@ int trace2_cmd_exit_fl(const char *file, int line, int code);
  * an annotation, and does nothing in non-leak-checking builds.
  */
 #ifdef SUPPRESS_ANNOTATED_LEAKS
-extern void unleak_memory(const void *ptr, size_t len);
+void unleak_memory(const void *ptr, size_t len);
 #define UNLEAK(var) unleak_memory(&(var), sizeof(var))
 #else
 #define UNLEAK(var) do {} while (0)
diff --git a/git-remote-testgit.sh b/git-remote-testgit.sh
deleted file mode 100755 (executable)
index 752c763..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/bin/sh
-# Copyright (c) 2012 Felipe Contreras
-
-# The first argument can be a url when the fetch/push command was a url
-# instead of a configured remote. In this case, use a generic alias.
-if test "$1" = "testgit::$2"; then
-       alias=_
-else
-       alias=$1
-fi
-url=$2
-
-dir="$GIT_DIR/testgit/$alias"
-prefix="refs/testgit/$alias"
-
-default_refspec="refs/heads/*:${prefix}/heads/*"
-
-refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}"
-
-test -z "$refspec" && prefix="refs"
-
-GIT_DIR="$url/.git"
-export GIT_DIR
-
-force=
-
-mkdir -p "$dir"
-
-if test -z "$GIT_REMOTE_TESTGIT_NO_MARKS"
-then
-       gitmarks="$dir/git.marks"
-       testgitmarks="$dir/testgit.marks"
-       test -e "$gitmarks" || >"$gitmarks"
-       test -e "$testgitmarks" || >"$testgitmarks"
-fi
-
-while read line
-do
-       case $line in
-       capabilities)
-               echo 'import'
-               echo 'export'
-               test -n "$refspec" && echo "refspec $refspec"
-               if test -n "$gitmarks"
-               then
-                       echo "*import-marks $gitmarks"
-                       echo "*export-marks $gitmarks"
-               fi
-               test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags"
-               test -n "$GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE" && echo "no-private-update"
-               echo 'option'
-               echo
-               ;;
-       list)
-               git for-each-ref --format='? %(refname)' 'refs/heads/'
-               head=$(git symbolic-ref HEAD)
-               echo "@$head HEAD"
-               echo
-               ;;
-       import*)
-               # read all import lines
-               while true
-               do
-                       ref="${line#* }"
-                       refs="$refs $ref"
-                       read line
-                       test "${line%% *}" != "import" && break
-               done
-
-               if test -n "$gitmarks"
-               then
-                       echo "feature import-marks=$gitmarks"
-                       echo "feature export-marks=$gitmarks"
-               fi
-
-               if test -n "$GIT_REMOTE_TESTGIT_FAILURE"
-               then
-                       echo "feature done"
-                       exit 1
-               fi
-
-               echo "feature done"
-               git fast-export \
-                       ${testgitmarks:+"--import-marks=$testgitmarks"} \
-                       ${testgitmarks:+"--export-marks=$testgitmarks"} \
-                       $refs |
-               sed -e "s#refs/heads/#${prefix}/heads/#g"
-               echo "done"
-               ;;
-       export)
-               if test -n "$GIT_REMOTE_TESTGIT_FAILURE"
-               then
-                       # consume input so fast-export doesn't get SIGPIPE;
-                       # git would also notice that case, but we want
-                       # to make sure we are exercising the later
-                       # error checks
-                       while read line; do
-                               test "done" = "$line" && break
-                       done
-                       exit 1
-               fi
-
-               before=$(git for-each-ref --format=' %(refname) %(objectname) ')
-
-               git fast-import \
-                       ${force:+--force} \
-                       ${testgitmarks:+"--import-marks=$testgitmarks"} \
-                       ${testgitmarks:+"--export-marks=$testgitmarks"} \
-                       --quiet
-
-               # figure out which refs were updated
-               git for-each-ref --format='%(refname) %(objectname)' |
-               while read ref a
-               do
-                       case "$before" in
-                       *" $ref $a "*)
-                               continue ;;     # unchanged
-                       esac
-                       if test -z "$GIT_REMOTE_TESTGIT_PUSH_ERROR"
-                       then
-                               echo "ok $ref"
-                       else
-                               echo "error $ref $GIT_REMOTE_TESTGIT_PUSH_ERROR"
-                       fi
-               done
-
-               echo
-               ;;
-       option\ *)
-               read cmd opt val <<-EOF
-               $line
-               EOF
-               case $opt in
-               force)
-                       test $val = "true" && force="true" || force=
-                       echo "ok"
-                       ;;
-               *)
-                       echo "unsupported"
-                       ;;
-               esac
-               ;;
-       '')
-               exit
-               ;;
-       esac
-done
index e3c054bde5f1d6fb858071ba7bf2939253811e1f..c7f58c5756f7b6f78689d574c5a2177d1bd409b5 100755 (executable)
@@ -232,6 +232,13 @@ cmd_add()
                die "$(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
        fi
 
+       if test -d "$sm_path" &&
+               test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
+       then
+           git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
+           die "$(eval_gettext "'\$sm_path' does not have a commit checked out")"
+       fi
+
        if test -z "$force" &&
                ! git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" > /dev/null 2>&1
        then
diff --git a/git.c b/git.c
index 50da125c60a317d820cec18767ef1fc88efd56bf..1bf9c94550d9c232de2f89ef0f2b5a661e43cd79 100644 (file)
--- a/git.c
+++ b/git.c
@@ -570,7 +570,6 @@ static struct cmd_struct commands[] = {
        { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
        { "rm", cmd_rm, RUN_SETUP },
        { "send-pack", cmd_send_pack, RUN_SETUP },
-       { "serve", cmd_serve, RUN_SETUP },
        { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
        { "show", cmd_show, RUN_SETUP },
        { "show-branch", cmd_show_branch, RUN_SETUP },
diff --git a/grep.h b/grep.h
index fb0489372169d7f1563a09e050c8715542ce5963..1875880f37990f981865b0fab332cc6c6c4e5971 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -186,17 +186,17 @@ struct grep_opt {
        void *output_priv;
 };
 
-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 *, struct repository *repo, const char *prefix);
+void init_grep_defaults(struct repository *);
+int grep_config(const char *var, const char *value, void *);
+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);
-extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
-extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
-extern void compile_grep_patterns(struct grep_opt *opt);
-extern void free_grep_patterns(struct grep_opt *opt);
-extern int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size);
+void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
+void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
+void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
+void compile_grep_patterns(struct grep_opt *opt);
+void free_grep_patterns(struct grep_opt *opt);
+int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size);
 
 struct grep_source {
        char *name;
@@ -226,8 +226,8 @@ void grep_source_load_driver(struct grep_source *gs,
 
 int grep_source(struct grep_opt *opt, struct grep_source *gs);
 
-extern struct grep_opt *grep_opt_dup(const struct grep_opt *opt);
-extern int grep_threads_ok(const struct grep_opt *opt);
+struct grep_opt *grep_opt_dup(const struct grep_opt *opt);
+int grep_threads_ok(const struct grep_opt *opt);
 
 /*
  * Mutex used around access to the attributes machinery if
index d375d9cce779952057e3c5dc6340edd12fb3f99d..f95593b6cfd7cecfbcc4584d74ac040cb355c767 100644 (file)
--- a/hashmap.h
+++ b/hashmap.h
  * `memihash_cont` is a variant of `memihash` that allows a computation to be
  * continued with another chunk of data.
  */
-extern unsigned int strhash(const char *buf);
-extern unsigned int strihash(const char *buf);
-extern unsigned int memhash(const void *buf, size_t len);
-extern unsigned int memihash(const void *buf, size_t len);
-extern unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len);
+unsigned int strhash(const char *buf);
+unsigned int strihash(const char *buf);
+unsigned int memhash(const void *buf, size_t len);
+unsigned int memihash(const void *buf, size_t len);
+unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len);
 
 /*
  * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
@@ -216,7 +216,7 @@ struct hashmap {
  * parameter may be used to preallocate a sufficiently large table and thus
  * prevent expensive resizing. If 0, the table is dynamically resized.
  */
-extern void hashmap_init(struct hashmap *map,
+void hashmap_init(struct hashmap *map,
                         hashmap_cmp_fn equals_function,
                         const void *equals_function_data,
                         size_t initial_size);
@@ -227,7 +227,7 @@ extern void hashmap_init(struct hashmap *map,
  * If `free_entries` is true, each hashmap_entry in the map is freed as well
  * using stdlibs free().
  */
-extern void hashmap_free(struct hashmap *map, int free_entries);
+void hashmap_free(struct hashmap *map, int free_entries);
 
 /* hashmap_entry functions */
 
@@ -284,7 +284,7 @@ static inline unsigned int hashmap_get_size(struct hashmap *map)
  * If an entry with matching hash code is found, `key` and `keydata` are passed
  * to `hashmap_cmp_fn` to decide whether the entry matches the key.
  */
-extern void *hashmap_get(const struct hashmap *map, const void *key,
+void *hashmap_get(const struct hashmap *map, const void *key,
                         const void *keydata);
 
 /*
@@ -316,7 +316,7 @@ static inline void *hashmap_get_from_hash(const struct hashmap *map,
  * `entry` is the hashmap_entry to start the search from, obtained via a previous
  * call to `hashmap_get` or `hashmap_get_next`.
  */
-extern void *hashmap_get_next(const struct hashmap *map, const void *entry);
+void *hashmap_get_next(const struct hashmap *map, const void *entry);
 
 /*
  * Adds a hashmap entry. This allows to add duplicate entries (i.e.
@@ -325,7 +325,7 @@ extern void *hashmap_get_next(const struct hashmap *map, const void *entry);
  * `map` is the hashmap structure.
  * `entry` is the entry to add.
  */
-extern void hashmap_add(struct hashmap *map, void *entry);
+void hashmap_add(struct hashmap *map, void *entry);
 
 /*
  * Adds or replaces a hashmap entry. If the hashmap contains duplicate
@@ -335,7 +335,7 @@ extern void hashmap_add(struct hashmap *map, void *entry);
  * `entry` is the entry to add or replace.
  * Returns the replaced entry, or NULL if not found (i.e. the entry was added).
  */
-extern void *hashmap_put(struct hashmap *map, void *entry);
+void *hashmap_put(struct hashmap *map, void *entry);
 
 /*
  * Removes a hashmap entry matching the specified key. If the hashmap contains
@@ -344,7 +344,7 @@ extern void *hashmap_put(struct hashmap *map, void *entry);
  *
  * Argument explanation is the same as in `hashmap_get`.
  */
-extern void *hashmap_remove(struct hashmap *map, const void *key,
+void *hashmap_remove(struct hashmap *map, const void *key,
                const void *keydata);
 
 /*
@@ -365,10 +365,10 @@ struct hashmap_iter {
 };
 
 /* Initializes a `hashmap_iter` structure. */
-extern void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);
+void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);
 
 /* Returns the next hashmap_entry, or NULL if there are no more entries. */
-extern void *hashmap_iter_next(struct hashmap_iter *iter);
+void *hashmap_iter_next(struct hashmap_iter *iter);
 
 /* Initializes the iterator and returns the first entry, if any. */
 static inline void *hashmap_iter_first(struct hashmap *map,
@@ -429,7 +429,7 @@ static inline void hashmap_enable_item_counting(struct hashmap *map)
  *
  * Uses a hashmap to store the pool of interned strings.
  */
-extern const void *memintern(const void *data, size_t len);
+const void *memintern(const void *data, size_t len);
 static inline const char *strintern(const char *string)
 {
        return memintern(string, strlen(string));
diff --git a/help.h b/help.h
index a141e209aea22b19f12c5e27435c5ed773488177..b8780fbd0fc3d0daacf029b3c3fcdd9a41bc733e 100644 (file)
--- a/help.h
+++ b/help.h
@@ -19,31 +19,31 @@ static inline void mput_char(char c, unsigned int num)
                putchar(c);
 }
 
-extern void list_common_cmds_help(void);
-extern void list_all_cmds_help(void);
-extern void list_common_guides_help(void);
-extern void list_config_help(int for_human);
+void list_common_cmds_help(void);
+void list_all_cmds_help(void);
+void list_common_guides_help(void);
+void list_config_help(int for_human);
 
-extern void list_all_main_cmds(struct string_list *list);
-extern void list_all_other_cmds(struct string_list *list);
-extern void list_cmds_by_category(struct string_list *list,
-                                 const char *category);
-extern void list_cmds_by_config(struct string_list *list);
-extern const char *help_unknown_cmd(const char *cmd);
-extern void load_command_list(const char *prefix,
-                             struct cmdnames *main_cmds,
-                             struct cmdnames *other_cmds);
-extern void add_cmdname(struct cmdnames *cmds, const char *name, int len);
+void list_all_main_cmds(struct string_list *list);
+void list_all_other_cmds(struct string_list *list);
+void list_cmds_by_category(struct string_list *list,
+                          const char *category);
+void list_cmds_by_config(struct string_list *list);
+const char *help_unknown_cmd(const char *cmd);
+void load_command_list(const char *prefix,
+                      struct cmdnames *main_cmds,
+                      struct cmdnames *other_cmds);
+void add_cmdname(struct cmdnames *cmds, const char *name, int len);
 /* Here we require that excludes is a sorted list. */
-extern void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
-extern int is_in_cmdlist(struct cmdnames *cmds, const char *name);
-extern void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
+int is_in_cmdlist(struct cmdnames *cmds, const char *name);
+void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
 
 /*
  * call this to die(), when it is suspected that the user mistyped a
  * ref to the command, to give suggested "correct" refs.
  */
-extern void help_unknown_ref(const char *ref, const char *cmd, const char *error);
+void help_unknown_ref(const char *ref, const char *cmd, const char *error);
 
 static inline void list_config_item(struct string_list *list,
                                    const char *prefix,
diff --git a/http.h b/http.h
index ddfe52f0232150e9506577ade34dfeaab61fc3ca..b429f1cf042bbb61e97e41cd1aacb012c4db834e 100644 (file)
--- a/http.h
+++ b/http.h
@@ -75,18 +75,18 @@ struct buffer {
 };
 
 /* Curl request read/write callbacks */
-extern size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
-extern size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
-extern size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
 #ifndef NO_CURL_IOCTL
-extern curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
+curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
 #endif
 
 /* Slot lifecycle functions */
-extern struct active_request_slot *get_active_slot(void);
-extern int start_active_slot(struct active_request_slot *slot);
-extern void run_active_slot(struct active_request_slot *slot);
-extern void finish_all_active_slots(void);
+struct active_request_slot *get_active_slot(void);
+int start_active_slot(struct active_request_slot *slot);
+void run_active_slot(struct active_request_slot *slot);
+void finish_all_active_slots(void);
 
 /*
  * This will run one slot to completion in a blocking manner, similar to how
@@ -98,15 +98,15 @@ int run_one_slot(struct active_request_slot *slot,
                 struct slot_results *results);
 
 #ifdef USE_CURL_MULTI
-extern void fill_active_slots(void);
-extern void add_fill_function(void *data, int (*fill)(void *));
-extern void step_active_slots(void);
+void fill_active_slots(void);
+void add_fill_function(void *data, int (*fill)(void *));
+void step_active_slots(void);
 #endif
 
-extern void http_init(struct remote *remote, const char *url,
-                     int proactive_auth);
-extern void http_cleanup(void);
-extern struct curl_slist *http_copy_default_headers(void);
+void http_init(struct remote *remote, const char *url,
+              int proactive_auth);
+void http_cleanup(void);
+struct curl_slist *http_copy_default_headers(void);
 
 extern long int git_curl_ipresolve;
 extern int active_requests;
@@ -146,11 +146,11 @@ void normalize_curl_result(CURLcode *result, long http_code, char *errorstr,
                           size_t errorlen);
 
 /* Helpers for modifying and creating URLs */
-extern void append_remote_object_url(struct strbuf *buf, const char *url,
-                                    const char *hex,
-                                    int only_two_digit_prefix);
-extern char *get_remote_object_url(const char *url, const char *hex,
-                                  int only_two_digit_prefix);
+void append_remote_object_url(struct strbuf *buf, const char *url,
+                             const char *hex,
+                             int only_two_digit_prefix);
+char *get_remote_object_url(const char *url, const char *hex,
+                           int only_two_digit_prefix);
 
 /* Options for http_get_*() */
 struct http_get_options {
@@ -204,11 +204,11 @@ struct http_get_options {
  */
 int http_get_strbuf(const char *url, struct strbuf *result, struct http_get_options *options);
 
-extern int http_fetch_ref(const char *base, struct ref *ref);
+int http_fetch_ref(const char *base, struct ref *ref);
 
 /* Helpers for fetching packs */
-extern int http_get_info_packs(const char *base_url,
-       struct packed_git **packs_head);
+int http_get_info_packs(const char *base_url,
+                       struct packed_git **packs_head);
 
 struct http_pack_request {
        char *url;
@@ -219,10 +219,10 @@ struct http_pack_request {
        struct active_request_slot *slot;
 };
 
-extern struct http_pack_request *new_http_pack_request(
+struct http_pack_request *new_http_pack_request(
        struct packed_git *target, const char *base_url);
-extern int finish_http_pack_request(struct http_pack_request *preq);
-extern void release_http_pack_request(struct http_pack_request *preq);
+int finish_http_pack_request(struct http_pack_request *preq);
+void release_http_pack_request(struct http_pack_request *preq);
 
 /* Helpers for fetching object */
 struct http_object_request {
@@ -241,12 +241,12 @@ struct http_object_request {
        struct active_request_slot *slot;
 };
 
-extern struct http_object_request *new_http_object_request(
+struct http_object_request *new_http_object_request(
        const char *base_url, const struct object_id *oid);
-extern void process_http_object_request(struct http_object_request *freq);
-extern int finish_http_object_request(struct http_object_request *freq);
-extern void abort_http_object_request(struct http_object_request *freq);
-extern void release_http_object_request(struct http_object_request *freq);
+void process_http_object_request(struct http_object_request *freq);
+int finish_http_object_request(struct http_object_request *freq);
+void abort_http_object_request(struct http_object_request *freq);
+void release_http_object_request(struct http_object_request *freq);
 
 /* setup routine for curl_easy_setopt CURLOPT_DEBUGFUNCTION */
 void setup_curl_trace(CURL *handle);
diff --git a/khash.h b/khash.h
index a09163b3e3beef8fe90f85003aae974411f6998a..af747a683fed88d9c69c5e2571150adc89cb886e 100644 (file)
--- a/khash.h
+++ b/khash.h
@@ -69,14 +69,14 @@ static const double __ac_HASH_UPPER = 0.77;
                khval_t *vals; \
        } kh_##name##_t;
 
-#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)                                             \
-       extern kh_##name##_t *kh_init_##name(void);                                                     \
-       extern void kh_destroy_##name(kh_##name##_t *h);                                        \
-       extern void kh_clear_##name(kh_##name##_t *h);                                          \
-       extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key);      \
-       extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
-       extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
-       extern void kh_del_##name(kh_##name##_t *h, khint_t x);
+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)                             \
+       kh_##name##_t *kh_init_##name(void);                                            \
+       void kh_destroy_##name(kh_##name##_t *h);                                       \
+       void kh_clear_##name(kh_##name##_t *h);                                         \
+       khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
+       int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
+       khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
+       void kh_del_##name(kh_##name##_t *h, khint_t x);
 
 #define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
        SCOPE kh_##name##_t *kh_init_##name(void) {                                                     \
diff --git a/kwset.h b/kwset.h
index 583f6268ef0beb121e8b776f38b4169784caf7a8..df99a92178231a53b4b03d7ee1f4dc1d1988a0c8 100644 (file)
--- a/kwset.h
+++ b/kwset.h
@@ -37,16 +37,16 @@ typedef struct kwset_t* kwset_t;
    if enough memory cannot be obtained.  The argument if non-NULL
    specifies a table of character translations to be applied to all
    pattern and search text. */
-extern kwset_t kwsalloc(unsigned char const *);
+kwset_t kwsalloc(unsigned char const *);
 
 /* Incrementally extend the keyword set to include the given string.
    Return NULL for success, or an error message.  Remember an index
    number for each keyword included in the set. */
-extern const char *kwsincr(kwset_t, char const *, size_t);
+const char *kwsincr(kwset_t, char const *, size_t);
 
 /* When the keyword set has been completely built, prepare it for
    use.  Return NULL for success, or an error message. */
-extern const char *kwsprep(kwset_t);
+const char *kwsprep(kwset_t);
 
 /* Search through the given buffer for a member of the keyword set.
    Return a pointer to the leftmost longest match found, or NULL if
@@ -54,8 +54,8 @@ extern const char *kwsprep(kwset_t);
    the matching substring in the integer it points to.  Similarly,
    if foundindex is non-NULL, store the index of the particular
    keyword found therein. */
-extern size_t kwsexec(kwset_t, char const *, size_t, struct kwsmatch *);
+size_t kwsexec(kwset_t, char const *, size_t, struct kwsmatch *);
 
 /* Deallocate the given keyword set and all its associated storage. */
-extern void kwsfree(kwset_t);
+void kwsfree(kwset_t);
 
index 59248e37cc3cec8fd309df77fb58b09c8612f336..0a17b21187b6620c3c5c82e53271d56bc0c6deb8 100644 (file)
@@ -498,7 +498,7 @@ static struct commit *check_single_commit(struct rev_info *revs)
 
 static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec)
 {
-       unsigned mode;
+       unsigned short mode;
        struct object_id oid;
 
        if (get_tree_entry(&commit->object.oid, spec->path, &oid, &mode))
index e2a5ee7c6d077df353a20d2d55d8b4fc24642bc3..8ee7a2bd4a1866be85d1cbe412be83fcbc5cc42a 100644 (file)
@@ -25,17 +25,17 @@ struct diff_ranges {
        struct range_set target;
 };
 
-extern void range_set_init(struct range_set *, size_t prealloc);
-extern void range_set_release(struct range_set *);
+void range_set_init(struct range_set *, size_t prealloc);
+void range_set_release(struct range_set *);
 /* Range includes start; excludes end */
-extern void range_set_append_unsafe(struct range_set *, long start, long end);
+void range_set_append_unsafe(struct range_set *, long start, long end);
 /* New range must begin at or after end of last added range */
-extern void range_set_append(struct range_set *, long start, long end);
+void range_set_append(struct range_set *, long start, long end);
 /*
  * In-place pass of sorting and merging the ranges in the range set,
  * to sort and make the ranges disjoint.
  */
-extern void sort_and_merge_range_set(struct range_set *);
+void sort_and_merge_range_set(struct range_set *);
 
 /* Linked list of interesting files and their associated ranges.  The
  * list must be kept sorted by path.
@@ -54,10 +54,10 @@ struct line_log_data {
        struct diff_ranges diff;
 };
 
-extern void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args);
+void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args);
 
-extern int line_log_filter(struct rev_info *rev);
+int line_log_filter(struct rev_info *rev);
 
-extern int line_log_print(struct rev_info *rev, struct commit *commit);
+int line_log_print(struct rev_info *rev, struct commit *commit);
 
 #endif /* LINE_LOG_H */
index dc77361e11d02a4eaa994f4351310a0d54d33ef6..b5651ddd5bfdd6cda4047f71cc3cb66fc31a793d 100644 (file)
@@ -125,6 +125,11 @@ static void process_tree_contents(struct traversal_context *ctx,
 
                if (S_ISDIR(entry.mode)) {
                        struct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);
+                       if (!t) {
+                               die(_("entry '%s' in tree %s has tree mode, "
+                                     "but is not a tree"),
+                                   entry.path, oid_to_hex(&tree->object.oid));
+                       }
                        t->object.flags |= NOT_USER_GIVEN;
                        process_tree(ctx, t, base, entry.path);
                }
@@ -133,6 +138,11 @@ static void process_tree_contents(struct traversal_context *ctx,
                                        base, entry.path);
                else {
                        struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);
+                       if (!b) {
+                               die(_("entry '%s' in tree %s has blob mode, "
+                                     "but is not a blob"),
+                                   entry.path, oid_to_hex(&tree->object.oid));
+                       }
                        b->object.flags |= NOT_USER_GIVEN;
                        process_blob(ctx, b, base, entry.path);
                }
@@ -364,6 +374,9 @@ static void do_traverse(struct traversal_context *ctx)
                        struct tree *tree = get_commit_tree(commit);
                        tree->object.flags |= NOT_USER_GIVEN;
                        add_pending_tree(ctx->revs, tree);
+               } else if (commit->object.parsed) {
+                       die(_("unable to load root tree for commit %s"),
+                             oid_to_hex(&commit->object.oid));
                }
                ctx->show_commit(commit, ctx->show_data);
 
index 35403ccc0d586c4732411a5813cb97761dddc435..9843053ce8940391834a86b122d1f64d5b345edf 100644 (file)
@@ -159,7 +159,7 @@ struct lock_file {
  * timeout_ms is -1, retry indefinitely. The flags argument and error
  * handling are described above.
  */
-extern int hold_lock_file_for_update_timeout(
+int hold_lock_file_for_update_timeout(
                struct lock_file *lk, const char *path,
                int flags, long timeout_ms);
 
@@ -188,8 +188,8 @@ static inline int is_lock_file_locked(struct lock_file *lk)
  * of `hold_lock_file_for_update()` to lock `path`. `err` should be the
  * `errno` set by the failing call.
  */
-extern void unable_to_lock_message(const char *path, int err,
-                                  struct strbuf *buf);
+void unable_to_lock_message(const char *path, int err,
+                           struct strbuf *buf);
 
 /*
  * Emit an appropriate error message and `die()` following the failure
@@ -197,7 +197,7 @@ extern void unable_to_lock_message(const char *path, int err,
  * `errno` set by the failing
  * call.
  */
-extern NORETURN void unable_to_lock_die(const char *path, int err);
+NORETURN void unable_to_lock_die(const char *path, int err);
 
 /*
  * Associate a stdio stream with the lockfile (which must still be
@@ -234,7 +234,7 @@ static inline FILE *get_lock_file_fp(struct lock_file *lk)
  * Return the path of the file that is locked by the specified
  * lock_file object. The caller must free the memory.
  */
-extern char *get_locked_file_path(struct lock_file *lk);
+char *get_locked_file_path(struct lock_file *lk);
 
 /*
  * If the lockfile is still open, close it (and the file pointer if it
@@ -282,7 +282,7 @@ static inline int reopen_lock_file(struct lock_file *lk)
  * call `commit_lock_file()` for a `lock_file` object that is not
  * currently locked.
  */
-extern int commit_lock_file(struct lock_file *lk);
+int commit_lock_file(struct lock_file *lk);
 
 /*
  * Like `commit_lock_file()`, but rename the lockfile to the provided
index b62877e8dad8d2ef3088af426f3ff6f8a97187f9..7e5646f5f62b2c69e591cde485b58c5328b609bc 100644 (file)
--- a/ls-refs.h
+++ b/ls-refs.h
@@ -4,7 +4,7 @@
 struct repository;
 struct argv_array;
 struct packet_reader;
-extern int ls_refs(struct repository *r, struct argv_array *keys,
-                  struct packet_reader *request);
+int ls_refs(struct repository *r, struct argv_array *keys,
+           struct packet_reader *request);
 
 #endif /* LS_REFS_H */
index 6830e1e6259bb050cf2af9c2e203c8aa52037da1..79b1d6774ec9ea8a2ef7a6303abb9fc1c917c36f 100644 (file)
@@ -39,8 +39,8 @@ struct mailinfo {
        int input_error;
 };
 
-extern void setup_mailinfo(struct mailinfo *);
-extern int mailinfo(struct mailinfo *, const char *msg, const char *patch);
-extern void clear_mailinfo(struct mailinfo *);
+void setup_mailinfo(struct mailinfo *);
+int mailinfo(struct mailinfo *, const char *msg, const char *patch);
+void clear_mailinfo(struct mailinfo *);
 
 #endif /* MAILINFO_H */
index ddc4d398456363cede9f3f5d4fa602efb402b5bd..9d1ec8d6b01e13b20b34c836130cd9557fdd1d80 100644 (file)
@@ -140,7 +140,7 @@ static void match_trees(const struct object_id *hash1,
        while (one.size) {
                const char *path;
                const struct object_id *elem;
-               unsigned mode;
+               unsigned short mode;
                int score;
 
                elem = tree_entry_extract(&one, &path, &mode);
@@ -196,7 +196,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
        rewrite_here = NULL;
        while (desc.size) {
                const char *name;
-               unsigned mode;
+               unsigned short mode;
 
                tree_entry_extract(&desc, &name, &mode);
                if (strlen(name) == toplen &&
@@ -285,7 +285,7 @@ void shift_tree(const struct object_id *hash1,
 
        if (add_score < del_score) {
                /* We need to pick a subtree of two */
-               unsigned mode;
+               unsigned short mode;
 
                if (!*del_prefix)
                        return;
@@ -313,7 +313,7 @@ void shift_tree_by(const struct object_id *hash1,
                   const char *shift_prefix)
 {
        struct object_id sub1, sub2;
-       unsigned mode1, mode2;
+       unsigned short mode1, mode2;
        unsigned candidate = 0;
 
        /* Can hash2 be a tree at shift_prefix in tree hash1? */
index cc31038b807fbee073a063ec83fe298c87fd2b3d..13cf9669e5b2c43b34bbecd2ea71bf14d9a36d65 100644 (file)
@@ -4,8 +4,8 @@
 struct blob;
 struct index_state;
 
-extern void *merge_blobs(struct index_state *, const char *,
-                        struct blob *, struct blob *,
-                        struct blob *, unsigned long *);
+void *merge_blobs(struct index_state *, const char *,
+                 struct blob *, struct blob *,
+                 struct blob *, unsigned long *);
 
 #endif /* MERGE_BLOBS_H */
index 6126773a7bfbd21ef38471d69d36b262e50a4050..a7bcfcbeb44679057a0429fd6641627796ad6d40 100644 (file)
@@ -115,32 +115,32 @@ static void collision_init(struct hashmap *map)
        hashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0);
 }
 
-static void flush_output(struct merge_options *o)
+static void flush_output(struct merge_options *opt)
 {
-       if (o->buffer_output < 2 && o->obuf.len) {
-               fputs(o->obuf.buf, stdout);
-               strbuf_reset(&o->obuf);
+       if (opt->buffer_output < 2 && opt->obuf.len) {
+               fputs(opt->obuf.buf, stdout);
+               strbuf_reset(&opt->obuf);
        }
 }
 
-static int err(struct merge_options *o, const char *err, ...)
+static int err(struct merge_options *opt, const char *err, ...)
 {
        va_list params;
 
-       if (o->buffer_output < 2)
-               flush_output(o);
+       if (opt->buffer_output < 2)
+               flush_output(opt);
        else {
-               strbuf_complete(&o->obuf, '\n');
-               strbuf_addstr(&o->obuf, "error: ");
+               strbuf_complete(&opt->obuf, '\n');
+               strbuf_addstr(&opt->obuf, "error: ");
        }
        va_start(params, err);
-       strbuf_vaddf(&o->obuf, err, params);
+       strbuf_vaddf(&opt->obuf, err, params);
        va_end(params);
-       if (o->buffer_output > 1)
-               strbuf_addch(&o->obuf, '\n');
+       if (opt->buffer_output > 1)
+               strbuf_addch(&opt->obuf, '\n');
        else {
-               error("%s", o->obuf.buf);
-               strbuf_reset(&o->obuf);
+               error("%s", opt->obuf.buf);
+               strbuf_reset(&opt->obuf);
        }
 
        return -1;
@@ -163,6 +163,11 @@ static struct tree *shift_tree_object(struct repository *repo,
        return lookup_tree(repo, &shifted);
 }
 
+static inline void set_commit_tree(struct commit *c, struct tree *t)
+{
+       c->maybe_tree = t;
+}
+
 static struct commit *make_virtual_commit(struct repository *repo,
                                          struct tree *tree,
                                          const char *comment)
@@ -170,7 +175,7 @@ static struct commit *make_virtual_commit(struct repository *repo,
        struct commit *commit = alloc_commit_node(repo);
 
        set_merge_remote_desc(commit, comment, (struct object *)commit);
-       commit->maybe_tree = tree;
+       set_commit_tree(commit, tree);
        commit->object.parsed = 1;
        return commit;
 }
@@ -196,163 +201,148 @@ enum rename_type {
        RENAME_TWO_FILES_TO_ONE
 };
 
-struct rename_conflict_info {
-       enum rename_type rename_type;
-       struct diff_filepair *pair1;
-       struct diff_filepair *pair2;
-       const char *branch1;
-       const char *branch2;
-       struct stage_data *dst_entry1;
-       struct stage_data *dst_entry2;
-       struct diff_filespec ren1_other;
-       struct diff_filespec ren2_other;
-};
-
 /*
  * Since we want to write the index eventually, we cannot reuse the index
  * for these (temporary) data.
  */
 struct stage_data {
-       struct {
-               unsigned mode;
-               struct object_id oid;
-       } stages[4];
+       struct diff_filespec stages[4]; /* mostly for oid & mode; maybe path */
        struct rename_conflict_info *rename_conflict_info;
        unsigned processed:1;
 };
 
+struct rename {
+       unsigned processed:1;
+       struct diff_filepair *pair;
+       const char *branch; /* branch that the rename occurred on */
+       /*
+        * If directory rename detection affected this rename, what was its
+        * original type ('A' or 'R') and it's original destination before
+        * the directory rename (otherwise, '\0' and NULL for these two vars).
+        */
+       char dir_rename_original_type;
+       char *dir_rename_original_dest;
+       /*
+        * Purpose of src_entry and dst_entry:
+        *
+        * If 'before' is renamed to 'after' then src_entry will contain
+        * the versions of 'before' from the merge_base, HEAD, and MERGE in
+        * stages 1, 2, and 3; dst_entry will contain the respective
+        * versions of 'after' in corresponding locations.  Thus, we have a
+        * total of six modes and oids, though some will be null.  (Stage 0
+        * is ignored; we're interested in handling conflicts.)
+        *
+        * Since we don't turn on break-rewrites by default, neither
+        * src_entry nor dst_entry can have all three of their stages have
+        * non-null oids, meaning at most four of the six will be non-null.
+        * Also, since this is a rename, both src_entry and dst_entry will
+        * have at least one non-null oid, meaning at least two will be
+        * non-null.  Of the six oids, a typical rename will have three be
+        * non-null.  Only two implies a rename/delete, and four implies a
+        * rename/add.
+        */
+       struct stage_data *src_entry;
+       struct stage_data *dst_entry;
+};
+
+struct rename_conflict_info {
+       enum rename_type rename_type;
+       struct rename *ren1;
+       struct rename *ren2;
+};
+
 static inline void setup_rename_conflict_info(enum rename_type rename_type,
-                                             struct diff_filepair *pair1,
-                                             struct diff_filepair *pair2,
-                                             const char *branch1,
-                                             const char *branch2,
-                                             struct stage_data *dst_entry1,
-                                             struct stage_data *dst_entry2,
-                                             struct merge_options *o,
-                                             struct stage_data *src_entry1,
-                                             struct stage_data *src_entry2)
-{
-       int ostage1 = 0, ostage2;
+                                             struct merge_options *opt,
+                                             struct rename *ren1,
+                                             struct rename *ren2)
+{
        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
+        * ensure that branch1 == opt->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);
+       if (ren2 && ren1->branch != opt->branch1) {
+               setup_rename_conflict_info(rename_type, opt, ren2, ren1);
                return;
        }
 
        ci = xcalloc(1, sizeof(struct rename_conflict_info));
        ci->rename_type = rename_type;
-       ci->pair1 = pair1;
-       ci->branch1 = branch1;
-       ci->branch2 = branch2;
-
-       ci->dst_entry1 = dst_entry1;
-       dst_entry1->rename_conflict_info = ci;
-       dst_entry1->processed = 0;
-
-       assert(!pair2 == !dst_entry2);
-       if (dst_entry2) {
-               ci->dst_entry2 = dst_entry2;
-               ci->pair2 = pair2;
-               dst_entry2->rename_conflict_info = ci;
-       }
+       ci->ren1 = ren1;
+       ci->ren2 = ren2;
 
-       /*
-        * For each rename, there could have been
-        * modifications on the side of history where that
-        * file was not renamed.
-        */
-       if (rename_type == RENAME_ADD ||
-           rename_type == RENAME_TWO_FILES_TO_ONE) {
-               ostage1 = o->branch1 == branch1 ? 3 : 2;
-
-               ci->ren1_other.path = pair1->one->path;
-               oidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid);
-               ci->ren1_other.mode = src_entry1->stages[ostage1].mode;
-       }
-
-       if (rename_type == RENAME_TWO_FILES_TO_ONE) {
-               ostage2 = ostage1 ^ 1;
-
-               ci->ren2_other.path = pair2->one->path;
-               oidcpy(&ci->ren2_other.oid, &src_entry2->stages[ostage2].oid);
-               ci->ren2_other.mode = src_entry2->stages[ostage2].mode;
+       ci->ren1->dst_entry->processed = 0;
+       ci->ren1->dst_entry->rename_conflict_info = ci;
+       if (ren2) {
+               ci->ren2->dst_entry->rename_conflict_info = ci;
        }
 }
 
-static int show(struct merge_options *o, int v)
+static int show(struct merge_options *opt, int v)
 {
-       return (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;
+       return (!opt->call_depth && opt->verbosity >= v) || opt->verbosity >= 5;
 }
 
 __attribute__((format (printf, 3, 4)))
-static void output(struct merge_options *o, int v, const char *fmt, ...)
+static void output(struct merge_options *opt, int v, const char *fmt, ...)
 {
        va_list ap;
 
-       if (!show(o, v))
+       if (!show(opt, v))
                return;
 
-       strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
+       strbuf_addchars(&opt->obuf, ' ', opt->call_depth * 2);
 
        va_start(ap, fmt);
-       strbuf_vaddf(&o->obuf, fmt, ap);
+       strbuf_vaddf(&opt->obuf, fmt, ap);
        va_end(ap);
 
-       strbuf_addch(&o->obuf, '\n');
-       if (!o->buffer_output)
-               flush_output(o);
+       strbuf_addch(&opt->obuf, '\n');
+       if (!opt->buffer_output)
+               flush_output(opt);
 }
 
-static void output_commit_title(struct merge_options *o, struct commit *commit)
+static void output_commit_title(struct merge_options *opt, struct commit *commit)
 {
        struct merge_remote_desc *desc;
 
-       strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
+       strbuf_addchars(&opt->obuf, ' ', opt->call_depth * 2);
        desc = merge_remote_util(commit);
        if (desc)
-               strbuf_addf(&o->obuf, "virtual %s\n", desc->name);
+               strbuf_addf(&opt->obuf, "virtual %s\n", desc->name);
        else {
-               strbuf_add_unique_abbrev(&o->obuf, &commit->object.oid,
+               strbuf_add_unique_abbrev(&opt->obuf, &commit->object.oid,
                                         DEFAULT_ABBREV);
-               strbuf_addch(&o->obuf, ' ');
+               strbuf_addch(&opt->obuf, ' ');
                if (parse_commit(commit) != 0)
-                       strbuf_addstr(&o->obuf, _("(bad commit)\n"));
+                       strbuf_addstr(&opt->obuf, _("(bad commit)\n"));
                else {
                        const char *title;
                        const char *msg = get_commit_buffer(commit, NULL);
                        int len = find_commit_subject(msg, &title);
                        if (len)
-                               strbuf_addf(&o->obuf, "%.*s\n", len, title);
+                               strbuf_addf(&opt->obuf, "%.*s\n", len, title);
                        unuse_commit_buffer(commit, msg);
                }
        }
-       flush_output(o);
+       flush_output(opt);
 }
 
-static int add_cacheinfo(struct merge_options *o,
-                        unsigned int mode, const struct object_id *oid,
+static int add_cacheinfo(struct merge_options *opt,
+                        const struct diff_filespec *blob,
                         const char *path, int stage, int refresh, int options)
 {
-       struct index_state *istate = o->repo->index;
+       struct index_state *istate = opt->repo->index;
        struct cache_entry *ce;
        int ret;
 
-       ce = make_cache_entry(istate, mode, oid ? oid : &null_oid, path, stage, 0);
+       ce = make_cache_entry(istate, blob->mode, &blob->oid, path, stage, 0);
        if (!ce)
-               return err(o, _("add_cacheinfo failed for path '%s'; merge aborting."), path);
+               return err(opt, _("add_cacheinfo failed for path '%s'; merge aborting."), path);
 
        ret = add_index_entry(istate, ce, options);
        if (refresh) {
@@ -361,7 +351,7 @@ static int add_cacheinfo(struct merge_options *o,
                nce = refresh_cache_entry(istate, ce,
                                          CE_MATCH_REFRESH | CE_MATCH_IGNORE_MISSING);
                if (!nce)
-                       return err(o, _("add_cacheinfo failed to refresh for path '%s'; merge aborting."), path);
+                       return err(opt, _("add_cacheinfo failed to refresh for path '%s'; merge aborting."), path);
                if (nce != ce)
                        ret = add_index_entry(istate, nce, options);
        }
@@ -374,7 +364,7 @@ static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
        init_tree_desc(desc, tree->buffer, tree->size);
 }
 
-static int unpack_trees_start(struct merge_options *o,
+static int unpack_trees_start(struct merge_options *opt,
                              struct tree *common,
                              struct tree *head,
                              struct tree *merge)
@@ -383,49 +373,49 @@ static int unpack_trees_start(struct merge_options *o,
        struct tree_desc t[3];
        struct index_state tmp_index = { NULL };
 
-       memset(&o->unpack_opts, 0, sizeof(o->unpack_opts));
-       if (o->call_depth)
-               o->unpack_opts.index_only = 1;
+       memset(&opt->unpack_opts, 0, sizeof(opt->unpack_opts));
+       if (opt->call_depth)
+               opt->unpack_opts.index_only = 1;
        else
-               o->unpack_opts.update = 1;
-       o->unpack_opts.merge = 1;
-       o->unpack_opts.head_idx = 2;
-       o->unpack_opts.fn = threeway_merge;
-       o->unpack_opts.src_index = o->repo->index;
-       o->unpack_opts.dst_index = &tmp_index;
-       o->unpack_opts.aggressive = !merge_detect_rename(o);
-       setup_unpack_trees_porcelain(&o->unpack_opts, "merge");
+               opt->unpack_opts.update = 1;
+       opt->unpack_opts.merge = 1;
+       opt->unpack_opts.head_idx = 2;
+       opt->unpack_opts.fn = threeway_merge;
+       opt->unpack_opts.src_index = opt->repo->index;
+       opt->unpack_opts.dst_index = &tmp_index;
+       opt->unpack_opts.aggressive = !merge_detect_rename(opt);
+       setup_unpack_trees_porcelain(&opt->unpack_opts, "merge");
 
        init_tree_desc_from_tree(t+0, common);
        init_tree_desc_from_tree(t+1, head);
        init_tree_desc_from_tree(t+2, merge);
 
-       rc = unpack_trees(3, t, &o->unpack_opts);
-       cache_tree_free(&o->repo->index->cache_tree);
+       rc = unpack_trees(3, t, &opt->unpack_opts);
+       cache_tree_free(&opt->repo->index->cache_tree);
 
        /*
-        * Update o->repo->index to match the new results, AFTER saving a copy
-        * in o->orig_index.  Update src_index to point to the saved copy.
+        * Update opt->repo->index to match the new results, AFTER saving a copy
+        * in opt->orig_index.  Update src_index to point to the saved copy.
         * (verify_uptodate() checks src_index, and the original index is
         * the one that had the necessary modification timestamps.)
         */
-       o->orig_index = *o->repo->index;
-       *o->repo->index = tmp_index;
-       o->unpack_opts.src_index = &o->orig_index;
+       opt->orig_index = *opt->repo->index;
+       *opt->repo->index = tmp_index;
+       opt->unpack_opts.src_index = &opt->orig_index;
 
        return rc;
 }
 
-static void unpack_trees_finish(struct merge_options *o)
+static void unpack_trees_finish(struct merge_options *opt)
 {
-       discard_index(&o->orig_index);
-       clear_unpack_trees_porcelain(&o->unpack_opts);
+       discard_index(&opt->orig_index);
+       clear_unpack_trees_porcelain(&opt->unpack_opts);
 }
 
-struct tree *write_tree_from_memory(struct merge_options *o)
+struct tree *write_tree_from_memory(struct merge_options *opt)
 {
        struct tree *result = NULL;
-       struct index_state *istate = o->repo->index;
+       struct index_state *istate = opt->repo->index;
 
        if (unmerged_index(istate)) {
                int i;
@@ -444,11 +434,11 @@ struct tree *write_tree_from_memory(struct merge_options *o)
 
        if (!cache_tree_fully_valid(istate->cache_tree) &&
            cache_tree_update(istate, 0) < 0) {
-               err(o, _("error building trees"));
+               err(opt, _("error building trees"));
                return NULL;
        }
 
-       result = lookup_tree(o->repo, &istate->cache_tree->oid);
+       result = lookup_tree(opt->repo, &istate->cache_tree->oid);
 
        return result;
 }
@@ -459,37 +449,36 @@ static int save_files_dirs(const struct object_id *oid,
 {
        struct path_hashmap_entry *entry;
        int baselen = base->len;
-       struct merge_options *o = context;
+       struct merge_options *opt = context;
 
        strbuf_addstr(base, path);
 
        FLEX_ALLOC_MEM(entry, path, base->buf, base->len);
        hashmap_entry_init(entry, path_hash(entry->path));
-       hashmap_add(&o->current_file_dir_set, entry);
+       hashmap_add(&opt->current_file_dir_set, entry);
 
        strbuf_setlen(base, baselen);
        return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
 }
 
-static void get_files_dirs(struct merge_options *o, struct tree *tree)
+static void get_files_dirs(struct merge_options *opt, struct tree *tree)
 {
        struct pathspec match_all;
        memset(&match_all, 0, sizeof(match_all));
        read_tree_recursive(the_repository, tree, "", 0, 0,
-                           &match_all, save_files_dirs, o);
+                           &match_all, save_files_dirs, opt);
 }
 
 static int get_tree_entry_if_blob(const struct object_id *tree,
                                  const char *path,
-                                 struct object_id *hashy,
-                                 unsigned int *mode_o)
+                                 struct diff_filespec *dfs)
 {
        int ret;
 
-       ret = get_tree_entry(tree, path, hashy, mode_o);
-       if (S_ISDIR(*mode_o)) {
-               oidcpy(hashy, &null_oid);
-               *mode_o = 0;
+       ret = get_tree_entry(tree, path, &dfs->oid, &dfs->mode);
+       if (S_ISDIR(dfs->mode)) {
+               oidcpy(&dfs->oid, &null_oid);
+               dfs->mode = 0;
        }
        return ret;
 }
@@ -504,12 +493,9 @@ static struct stage_data *insert_stage_data(const char *path,
 {
        struct string_list_item *item;
        struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
-       get_tree_entry_if_blob(&o->object.oid, path,
-                              &e->stages[1].oid, &e->stages[1].mode);
-       get_tree_entry_if_blob(&a->object.oid, path,
-                              &e->stages[2].oid, &e->stages[2].mode);
-       get_tree_entry_if_blob(&b->object.oid, path,
-                              &e->stages[3].oid, &e->stages[3].mode);
+       get_tree_entry_if_blob(&o->object.oid, path, &e->stages[1]);
+       get_tree_entry_if_blob(&a->object.oid, path, &e->stages[2]);
+       get_tree_entry_if_blob(&b->object.oid, path, &e->stages[3]);
        item = string_list_insert(entries, path);
        item->util = e;
        return e;
@@ -573,7 +559,7 @@ static int string_list_df_name_compare(const char *one, const char *two)
        return onelen - twolen;
 }
 
-static void record_df_conflict_files(struct merge_options *o,
+static void record_df_conflict_files(struct merge_options *opt,
                                     struct string_list *entries)
 {
        /* If there is a D/F conflict and the file for such a conflict
@@ -598,7 +584,7 @@ static void record_df_conflict_files(struct merge_options *o,
         * If we're merging merge-bases, we don't want to bother with
         * any working directory changes.
         */
-       if (o->call_depth)
+       if (opt->call_depth)
                return;
 
        /* Ensure D/F conflicts are adjacent in the entries list. */
@@ -610,7 +596,7 @@ static void record_df_conflict_files(struct merge_options *o,
        df_sorted_entries.cmp = string_list_df_name_compare;
        string_list_sort(&df_sorted_entries);
 
-       string_list_clear(&o->df_conflict_file_set, 1);
+       string_list_clear(&opt->df_conflict_file_set, 1);
        for (i = 0; i < df_sorted_entries.nr; i++) {
                const char *path = df_sorted_entries.items[i].string;
                int len = strlen(path);
@@ -626,7 +612,7 @@ static void record_df_conflict_files(struct merge_options *o,
                    len > last_len &&
                    memcmp(path, last_file, last_len) == 0 &&
                    path[last_len] == '/') {
-                       string_list_insert(&o->df_conflict_file_set, last_file);
+                       string_list_insert(&opt->df_conflict_file_set, last_file);
                }
 
                /*
@@ -645,33 +631,6 @@ static void record_df_conflict_files(struct merge_options *o,
        string_list_clear(&df_sorted_entries, 0);
 }
 
-struct rename {
-       struct diff_filepair *pair;
-       /*
-        * Purpose of src_entry and dst_entry:
-        *
-        * If 'before' is renamed to 'after' then src_entry will contain
-        * the versions of 'before' from the merge_base, HEAD, and MERGE in
-        * stages 1, 2, and 3; dst_entry will contain the respective
-        * versions of 'after' in corresponding locations.  Thus, we have a
-        * total of six modes and oids, though some will be null.  (Stage 0
-        * is ignored; we're interested in handling conflicts.)
-        *
-        * Since we don't turn on break-rewrites by default, neither
-        * src_entry nor dst_entry can have all three of their stages have
-        * non-null oids, meaning at most four of the six will be non-null.
-        * Also, since this is a rename, both src_entry and dst_entry will
-        * have at least one non-null oid, meaning at least two will be
-        * non-null.  Of the six oids, a typical rename will have three be
-        * non-null.  Only two implies a rename/delete, and four implies a
-        * rename/add.
-        */
-       struct stage_data *src_entry;
-       struct stage_data *dst_entry;
-       unsigned add_turned_into_rename:1;
-       unsigned processed:1;
-};
-
 static int update_stages(struct merge_options *opt, const char *path,
                         const struct diff_filespec *o,
                         const struct diff_filespec *a,
@@ -692,13 +651,13 @@ static int update_stages(struct merge_options *opt, const char *path,
                if (remove_file_from_index(opt->repo->index, path))
                        return -1;
        if (o)
-               if (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options))
+               if (add_cacheinfo(opt, o, path, 1, 0, options))
                        return -1;
        if (a)
-               if (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options))
+               if (add_cacheinfo(opt, a, path, 2, 0, options))
                        return -1;
        if (b)
-               if (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options))
+               if (add_cacheinfo(opt, b, path, 3, 0, options))
                        return -1;
        return 0;
 }
@@ -717,20 +676,20 @@ static void update_entry(struct stage_data *entry,
        oidcpy(&entry->stages[3].oid, &b->oid);
 }
 
-static int remove_file(struct merge_options *o, int clean,
+static int remove_file(struct merge_options *opt, int clean,
                       const char *path, int no_wd)
 {
-       int update_cache = o->call_depth || clean;
-       int update_working_directory = !o->call_depth && !no_wd;
+       int update_cache = opt->call_depth || clean;
+       int update_working_directory = !opt->call_depth && !no_wd;
 
        if (update_cache) {
-               if (remove_file_from_index(o->repo->index, path))
+               if (remove_file_from_index(opt->repo->index, path))
                        return -1;
        }
        if (update_working_directory) {
                if (ignore_case) {
                        struct cache_entry *ce;
-                       ce = index_file_exists(o->repo->index, path, strlen(path),
+                       ce = index_file_exists(opt->repo->index, path, strlen(path),
                                               ignore_case);
                        if (ce && ce_stage(ce) == 0 && strcmp(path, ce->name))
                                return 0;
@@ -751,7 +710,7 @@ static void add_flattened_path(struct strbuf *out, const char *s)
                        out->buf[i] = '_';
 }
 
-static char *unique_path(struct merge_options *o, const char *path, const char *branch)
+static char *unique_path(struct merge_options *opt, const char *path, const char *branch)
 {
        struct path_hashmap_entry *entry;
        struct strbuf newpath = STRBUF_INIT;
@@ -762,16 +721,16 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
        add_flattened_path(&newpath, branch);
 
        base_len = newpath.len;
-       while (hashmap_get_from_hash(&o->current_file_dir_set,
+       while (hashmap_get_from_hash(&opt->current_file_dir_set,
                                     path_hash(newpath.buf), newpath.buf) ||
-              (!o->call_depth && file_exists(newpath.buf))) {
+              (!opt->call_depth && file_exists(newpath.buf))) {
                strbuf_setlen(&newpath, base_len);
                strbuf_addf(&newpath, "_%d", suffix++);
        }
 
        FLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len);
        hashmap_entry_init(entry, path_hash(entry->path));
-       hashmap_add(&o->current_file_dir_set, entry);
+       hashmap_add(&opt->current_file_dir_set, entry);
        return strbuf_detach(&newpath, NULL);
 }
 
@@ -810,10 +769,10 @@ static int dir_in_way(struct index_state *istate, const char *path,
  * Returns whether path was tracked in the index before the merge started,
  * and its oid and mode match the specified values
  */
-static int was_tracked_and_matches(struct merge_options *o, const char *path,
-                                  const struct object_id *oid, unsigned mode)
+static int was_tracked_and_matches(struct merge_options *opt, const char *path,
+                                  const struct diff_filespec *blob)
 {
-       int pos = index_name_pos(&o->orig_index, path, strlen(path));
+       int pos = index_name_pos(&opt->orig_index, path, strlen(path));
        struct cache_entry *ce;
 
        if (0 > pos)
@@ -821,16 +780,16 @@ static int was_tracked_and_matches(struct merge_options *o, const char *path,
                return 0;
 
        /* See if the file we were tracking before matches */
-       ce = o->orig_index.cache[pos];
-       return (oid_eq(&ce->oid, oid) && ce->ce_mode == mode);
+       ce = opt->orig_index.cache[pos];
+       return (oid_eq(&ce->oid, &blob->oid) && ce->ce_mode == blob->mode);
 }
 
 /*
  * Returns whether path was tracked in the index before the merge started
  */
-static int was_tracked(struct merge_options *o, const char *path)
+static int was_tracked(struct merge_options *opt, const char *path)
 {
-       int pos = index_name_pos(&o->orig_index, path, strlen(path));
+       int pos = index_name_pos(&opt->orig_index, path, strlen(path));
 
        if (0 <= pos)
                /* we were tracking this path before the merge */
@@ -839,13 +798,13 @@ static int was_tracked(struct merge_options *o, const char *path)
        return 0;
 }
 
-static int would_lose_untracked(struct merge_options *o, const char *path)
+static int would_lose_untracked(struct merge_options *opt, const char *path)
 {
-       struct index_state *istate = o->repo->index;
+       struct index_state *istate = opt->repo->index;
 
        /*
         * This may look like it can be simplified to:
-        *   return !was_tracked(o, path) && file_exists(path)
+        *   return !was_tracked(opt, path) && file_exists(path)
         * but it can't.  This function needs to know whether path was in
         * the working tree due to EITHER having been tracked in the index
         * before the merge OR having been put into the working copy and
@@ -882,38 +841,38 @@ static int would_lose_untracked(struct merge_options *o, const char *path)
        return file_exists(path);
 }
 
-static int was_dirty(struct merge_options *o, const char *path)
+static int was_dirty(struct merge_options *opt, const char *path)
 {
        struct cache_entry *ce;
        int dirty = 1;
 
-       if (o->call_depth || !was_tracked(o, path))
+       if (opt->call_depth || !was_tracked(opt, path))
                return !dirty;
 
-       ce = index_file_exists(o->unpack_opts.src_index,
+       ce = index_file_exists(opt->unpack_opts.src_index,
                               path, strlen(path), ignore_case);
-       dirty = verify_uptodate(ce, &o->unpack_opts) != 0;
+       dirty = verify_uptodate(ce, &opt->unpack_opts) != 0;
        return dirty;
 }
 
-static int make_room_for_path(struct merge_options *o, const char *path)
+static int make_room_for_path(struct merge_options *opt, const char *path)
 {
        int status, i;
        const char *msg = _("failed to create path '%s'%s");
 
        /* Unlink any D/F conflict files that are in the way */
-       for (i = 0; i < o->df_conflict_file_set.nr; i++) {
-               const char *df_path = o->df_conflict_file_set.items[i].string;
+       for (i = 0; i < opt->df_conflict_file_set.nr; i++) {
+               const char *df_path = opt->df_conflict_file_set.items[i].string;
                size_t pathlen = strlen(path);
                size_t df_pathlen = strlen(df_path);
                if (df_pathlen < pathlen &&
                    path[df_pathlen] == '/' &&
                    strncmp(path, df_path, df_pathlen) == 0) {
-                       output(o, 3,
+                       output(opt, 3,
                               _("Removing %s to make room for subdirectory\n"),
                               df_path);
                        unlink(df_path);
-                       unsorted_string_list_delete_item(&o->df_conflict_file_set,
+                       unsorted_string_list_delete_item(&opt->df_conflict_file_set,
                                                         i, 0);
                        break;
                }
@@ -924,16 +883,16 @@ static int make_room_for_path(struct merge_options *o, const char *path)
        if (status) {
                if (status == SCLD_EXISTS)
                        /* something else exists */
-                       return err(o, msg, path, _(": perhaps a D/F conflict?"));
-               return err(o, msg, path, "");
+                       return err(opt, msg, path, _(": perhaps a D/F conflict?"));
+               return err(opt, msg, path, "");
        }
 
        /*
         * Do not unlink a file in the work tree if we are not
         * tracking it.
         */
-       if (would_lose_untracked(o, path))
-               return err(o, _("refusing to lose untracked file at '%s'"),
+       if (would_lose_untracked(opt, path))
+               return err(opt, _("refusing to lose untracked file at '%s'"),
                           path);
 
        /* Successful unlink is good.. */
@@ -943,19 +902,18 @@ static int make_room_for_path(struct merge_options *o, const char *path)
        if (errno == ENOENT)
                return 0;
        /* .. but not some other error (who really cares what?) */
-       return err(o, msg, path, _(": perhaps a D/F conflict?"));
+       return err(opt, msg, path, _(": perhaps a D/F conflict?"));
 }
 
-static int update_file_flags(struct merge_options *o,
-                            const struct object_id *oid,
-                            unsigned mode,
+static int update_file_flags(struct merge_options *opt,
+                            const struct diff_filespec *contents,
                             const char *path,
                             int update_cache,
                             int update_wd)
 {
        int ret = 0;
 
-       if (o->call_depth)
+       if (opt->call_depth)
                update_wd = 0;
 
        if (update_wd) {
@@ -963,7 +921,7 @@ static int update_file_flags(struct merge_options *o,
                void *buf;
                unsigned long size;
 
-               if (S_ISGITLINK(mode)) {
+               if (S_ISGITLINK(contents->mode)) {
                        /*
                         * We may later decide to recursively descend into
                         * the submodule directory and update its index
@@ -973,84 +931,84 @@ static int update_file_flags(struct merge_options *o,
                        goto update_index;
                }
 
-               buf = read_object_file(oid, &type, &size);
+               buf = read_object_file(&contents->oid, &type, &size);
                if (!buf)
-                       return err(o, _("cannot read object %s '%s'"), oid_to_hex(oid), path);
+                       return err(opt, _("cannot read object %s '%s'"),
+                                  oid_to_hex(&contents->oid), path);
                if (type != OBJ_BLOB) {
-                       ret = err(o, _("blob expected for %s '%s'"), oid_to_hex(oid), path);
+                       ret = err(opt, _("blob expected for %s '%s'"),
+                                 oid_to_hex(&contents->oid), path);
                        goto free_buf;
                }
-               if (S_ISREG(mode)) {
+               if (S_ISREG(contents->mode)) {
                        struct strbuf strbuf = STRBUF_INIT;
-                       if (convert_to_working_tree(o->repo->index, path, buf, size, &strbuf)) {
+                       if (convert_to_working_tree(opt->repo->index, path, buf, size, &strbuf)) {
                                free(buf);
                                size = strbuf.len;
                                buf = strbuf_detach(&strbuf, NULL);
                        }
                }
 
-               if (make_room_for_path(o, path) < 0) {
+               if (make_room_for_path(opt, path) < 0) {
                        update_wd = 0;
                        goto free_buf;
                }
-               if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
+               if (S_ISREG(contents->mode) ||
+                   (!has_symlinks && S_ISLNK(contents->mode))) {
                        int fd;
-                       if (mode & 0100)
-                               mode = 0777;
-                       else
-                               mode = 0666;
+                       int mode = (contents->mode & 0100 ? 0777 : 0666);
+
                        fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
                        if (fd < 0) {
-                               ret = err(o, _("failed to open '%s': %s"),
+                               ret = err(opt, _("failed to open '%s': %s"),
                                          path, strerror(errno));
                                goto free_buf;
                        }
                        write_in_full(fd, buf, size);
                        close(fd);
-               } else if (S_ISLNK(mode)) {
+               } else if (S_ISLNK(contents->mode)) {
                        char *lnk = xmemdupz(buf, size);
                        safe_create_leading_directories_const(path);
                        unlink(path);
                        if (symlink(lnk, path))
-                               ret = err(o, _("failed to symlink '%s': %s"),
+                               ret = err(opt, _("failed to symlink '%s': %s"),
                                          path, strerror(errno));
                        free(lnk);
                } else
-                       ret = err(o,
+                       ret = err(opt,
                                  _("do not know what to do with %06o %s '%s'"),
-                                 mode, oid_to_hex(oid), path);
+                                 contents->mode, oid_to_hex(&contents->oid), path);
        free_buf:
                free(buf);
        }
 update_index:
        if (!ret && update_cache)
-               if (add_cacheinfo(o, mode, oid, path, 0, update_wd,
+               if (add_cacheinfo(opt, contents, path, 0, update_wd,
                                  ADD_CACHE_OK_TO_ADD))
                        return -1;
        return ret;
 }
 
-static int update_file(struct merge_options *o,
+static int update_file(struct merge_options *opt,
                       int clean,
-                      const struct object_id *oid,
-                      unsigned mode,
+                      const struct diff_filespec *contents,
                       const char *path)
 {
-       return update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);
+       return update_file_flags(opt, contents, path,
+                                opt->call_depth || clean, !opt->call_depth);
 }
 
 /* Low level file merging, update and removal */
 
 struct merge_file_info {
-       struct object_id oid;
-       unsigned mode;
+       struct diff_filespec blob; /* mostly use oid & mode; sometimes path */
        unsigned clean:1,
                 merge:1;
 };
 
-static int merge_3way(struct merge_options *o,
+static int merge_3way(struct merge_options *opt,
                      mmbuffer_t *result_buf,
-                     const struct diff_filespec *one,
+                     const struct diff_filespec *o,
                      const struct diff_filespec *a,
                      const struct diff_filespec *b,
                      const char *branch1,
@@ -1062,15 +1020,15 @@ static int merge_3way(struct merge_options *o,
        char *base_name, *name1, *name2;
        int merge_status;
 
-       ll_opts.renormalize = o->renormalize;
+       ll_opts.renormalize = opt->renormalize;
        ll_opts.extra_marker_size = extra_marker_size;
-       ll_opts.xdl_opts = o->xdl_opts;
+       ll_opts.xdl_opts = opt->xdl_opts;
 
-       if (o->call_depth) {
+       if (opt->call_depth) {
                ll_opts.virtual_ancestor = 1;
                ll_opts.variant = 0;
        } else {
-               switch (o->recursive_variant) {
+               switch (opt->recursive_variant) {
                case MERGE_RECURSIVE_OURS:
                        ll_opts.variant = XDL_MERGE_FAVOR_OURS;
                        break;
@@ -1083,26 +1041,27 @@ static int merge_3way(struct merge_options *o,
                }
        }
 
+       assert(a->path && b->path);
        if (strcmp(a->path, b->path) ||
-           (o->ancestor != NULL && strcmp(a->path, one->path) != 0)) {
-               base_name = o->ancestor == NULL ? NULL :
-                       mkpathdup("%s:%s", o->ancestor, one->path);
+           (opt->ancestor != NULL && strcmp(a->path, o->path) != 0)) {
+               base_name = opt->ancestor == NULL ? NULL :
+                       mkpathdup("%s:%s", opt->ancestor, o->path);
                name1 = mkpathdup("%s:%s", branch1, a->path);
                name2 = mkpathdup("%s:%s", branch2, b->path);
        } else {
-               base_name = o->ancestor == NULL ? NULL :
-                       mkpathdup("%s", o->ancestor);
+               base_name = opt->ancestor == NULL ? NULL :
+                       mkpathdup("%s", opt->ancestor);
                name1 = mkpathdup("%s", branch1);
                name2 = mkpathdup("%s", branch2);
        }
 
-       read_mmblob(&orig, &one->oid);
+       read_mmblob(&orig, &o->oid);
        read_mmblob(&src1, &a->oid);
        read_mmblob(&src2, &b->oid);
 
        merge_status = ll_merge(result_buf, a->path, &orig, base_name,
                                &src1, name1, &src2, name2,
-                               o->repo->index, &ll_opts);
+                               opt->repo->index, &ll_opts);
 
        free(base_name);
        free(name1);
@@ -1184,7 +1143,12 @@ static void print_commit(struct commit *commit)
        strbuf_release(&sb);
 }
 
-static int merge_submodule(struct merge_options *o,
+static int is_valid(const struct diff_filespec *dfs)
+{
+       return dfs->mode != 0 && !is_null_oid(&dfs->oid);
+}
+
+static int merge_submodule(struct merge_options *opt,
                           struct object_id *result, const char *path,
                           const struct object_id *base, const struct object_id *a,
                           const struct object_id *b)
@@ -1194,7 +1158,7 @@ static int merge_submodule(struct merge_options *o,
        struct object_array merges;
 
        int i;
-       int search = !o->call_depth;
+       int search = !opt->call_depth;
 
        /* store a in result in case we fail */
        oidcpy(result, a);
@@ -1208,32 +1172,32 @@ static int merge_submodule(struct merge_options *o,
                return 0;
 
        if (add_submodule_odb(path)) {
-               output(o, 1, _("Failed to merge submodule %s (not checked out)"), path);
+               output(opt, 1, _("Failed to merge submodule %s (not checked out)"), path);
                return 0;
        }
 
-       if (!(commit_base = lookup_commit_reference(o->repo, base)) ||
-           !(commit_a = lookup_commit_reference(o->repo, a)) ||
-           !(commit_b = lookup_commit_reference(o->repo, b))) {
-               output(o, 1, _("Failed to merge submodule %s (commits not present)"), path);
+       if (!(commit_base = lookup_commit_reference(opt->repo, base)) ||
+           !(commit_a = lookup_commit_reference(opt->repo, a)) ||
+           !(commit_b = lookup_commit_reference(opt->repo, b))) {
+               output(opt, 1, _("Failed to merge submodule %s (commits not present)"), path);
                return 0;
        }
 
        /* check whether both changes are forward */
        if (!in_merge_bases(commit_base, commit_a) ||
            !in_merge_bases(commit_base, commit_b)) {
-               output(o, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
+               output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
                return 0;
        }
 
        /* Case #1: a is contained in b or vice versa */
        if (in_merge_bases(commit_a, commit_b)) {
                oidcpy(result, b);
-               if (show(o, 3)) {
-                       output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
-                       output_commit_title(o, commit_b);
-               } else if (show(o, 2))
-                       output(o, 2, _("Fast-forwarding submodule %s"), path);
+               if (show(opt, 3)) {
+                       output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
+                       output_commit_title(opt, commit_b);
+               } else if (show(opt, 2))
+                       output(opt, 2, _("Fast-forwarding submodule %s"), path);
                else
                        ; /* no output */
 
@@ -1241,11 +1205,11 @@ static int merge_submodule(struct merge_options *o,
        }
        if (in_merge_bases(commit_b, commit_a)) {
                oidcpy(result, a);
-               if (show(o, 3)) {
-                       output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
-                       output_commit_title(o, commit_a);
-               } else if (show(o, 2))
-                       output(o, 2, _("Fast-forwarding submodule %s"), path);
+               if (show(opt, 3)) {
+                       output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
+                       output_commit_title(opt, commit_a);
+               } else if (show(opt, 2))
+                       output(opt, 2, _("Fast-forwarding submodule %s"), path);
                else
                        ; /* no output */
 
@@ -1264,18 +1228,18 @@ static int merge_submodule(struct merge_options *o,
                return 0;
 
        /* find commit which merges them */
-       parent_count = find_first_merges(o->repo, &merges, path,
+       parent_count = find_first_merges(opt->repo, &merges, path,
                                         commit_a, commit_b);
        switch (parent_count) {
        case 0:
-               output(o, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
+               output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
                break;
 
        case 1:
-               output(o, 1, _("Failed to merge submodule %s (not fast-forward)"), path);
-               output(o, 2, _("Found a possible merge resolution for the submodule:\n"));
+               output(opt, 1, _("Failed to merge submodule %s (not fast-forward)"), path);
+               output(opt, 2, _("Found a possible merge resolution for the submodule:\n"));
                print_commit((struct commit *) merges.objects[0].item);
-               output(o, 2, _(
+               output(opt, 2, _(
                       "If this is correct simply add it to the index "
                       "for example\n"
                       "by using:\n\n"
@@ -1285,7 +1249,7 @@ static int merge_submodule(struct merge_options *o,
                break;
 
        default:
-               output(o, 1, _("Failed to merge submodule %s (multiple merges found)"), path);
+               output(opt, 1, _("Failed to merge submodule %s (multiple merges found)"), path);
                for (i = 0; i < merges.nr; i++)
                        print_commit((struct commit *) merges.objects[i].item);
        }
@@ -1294,8 +1258,8 @@ static int merge_submodule(struct merge_options *o,
        return 0;
 }
 
-static int merge_mode_and_contents(struct merge_options *o,
-                                  const struct diff_filespec *one,
+static int merge_mode_and_contents(struct merge_options *opt,
+                                  const struct diff_filespec *o,
                                   const struct diff_filespec *a,
                                   const struct diff_filespec *b,
                                   const char *filename,
@@ -1304,13 +1268,13 @@ static int merge_mode_and_contents(struct merge_options *o,
                                   const int extra_marker_size,
                                   struct merge_file_info *result)
 {
-       if (o->branch1 != branch1) {
+       if (opt->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,
+               return merge_mode_and_contents(opt, o, b, a,
                                               filename,
                                               branch2, branch1,
                                               extra_marker_size, result);
@@ -1322,48 +1286,48 @@ static int merge_mode_and_contents(struct merge_options *o,
        if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {
                result->clean = 0;
                if (S_ISREG(a->mode)) {
-                       result->mode = a->mode;
-                       oidcpy(&result->oid, &a->oid);
+                       result->blob.mode = a->mode;
+                       oidcpy(&result->blob.oid, &a->oid);
                } else {
-                       result->mode = b->mode;
-                       oidcpy(&result->oid, &b->oid);
+                       result->blob.mode = b->mode;
+                       oidcpy(&result->blob.oid, &b->oid);
                }
        } else {
-               if (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid))
+               if (!oid_eq(&a->oid, &o->oid) && !oid_eq(&b->oid, &o->oid))
                        result->merge = 1;
 
                /*
                 * Merge modes
                 */
-               if (a->mode == b->mode || a->mode == one->mode)
-                       result->mode = b->mode;
+               if (a->mode == b->mode || a->mode == o->mode)
+                       result->blob.mode = b->mode;
                else {
-                       result->mode = a->mode;
-                       if (b->mode != one->mode) {
+                       result->blob.mode = a->mode;
+                       if (b->mode != o->mode) {
                                result->clean = 0;
                                result->merge = 1;
                        }
                }
 
-               if (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid))
-                       oidcpy(&result->oid, &b->oid);
-               else if (oid_eq(&b->oid, &one->oid))
-                       oidcpy(&result->oid, &a->oid);
+               if (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &o->oid))
+                       oidcpy(&result->blob.oid, &b->oid);
+               else if (oid_eq(&b->oid, &o->oid))
+                       oidcpy(&result->blob.oid, &a->oid);
                else if (S_ISREG(a->mode)) {
                        mmbuffer_t result_buf;
                        int ret = 0, merge_status;
 
-                       merge_status = merge_3way(o, &result_buf, one, a, b,
+                       merge_status = merge_3way(opt, &result_buf, o, a, b,
                                                  branch1, branch2,
                                                  extra_marker_size);
 
                        if ((merge_status < 0) || !result_buf.ptr)
-                               ret = err(o, _("Failed to execute internal merge"));
+                               ret = err(opt, _("Failed to execute internal merge"));
 
                        if (!ret &&
                            write_object_file(result_buf.ptr, result_buf.size,
-                                             blob_type, &result->oid))
-                               ret = err(o, _("Unable to add %s to database"),
+                                             blob_type, &result->blob.oid))
+                               ret = err(opt, _("Unable to add %s to database"),
                                          a->path);
 
                        free(result_buf.ptr);
@@ -1371,23 +1335,23 @@ static int merge_mode_and_contents(struct merge_options *o,
                                return ret;
                        result->clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
-                       result->clean = merge_submodule(o, &result->oid,
-                                                       one->path,
-                                                       &one->oid,
+                       result->clean = merge_submodule(opt, &result->blob.oid,
+                                                       o->path,
+                                                       &o->oid,
                                                        &a->oid,
                                                        &b->oid);
                } else if (S_ISLNK(a->mode)) {
-                       switch (o->recursive_variant) {
+                       switch (opt->recursive_variant) {
                        case MERGE_RECURSIVE_NORMAL:
-                               oidcpy(&result->oid, &a->oid);
+                               oidcpy(&result->blob.oid, &a->oid);
                                if (!oid_eq(&a->oid, &b->oid))
                                        result->clean = 0;
                                break;
                        case MERGE_RECURSIVE_OURS:
-                               oidcpy(&result->oid, &a->oid);
+                               oidcpy(&result->blob.oid, &a->oid);
                                break;
                        case MERGE_RECURSIVE_THEIRS:
-                               oidcpy(&result->oid, &b->oid);
+                               oidcpy(&result->blob.oid, &b->oid);
                                break;
                        }
                } else
@@ -1395,14 +1359,13 @@ static int merge_mode_and_contents(struct merge_options *o,
        }
 
        if (result->merge)
-               output(o, 2, _("Auto-merging %s"), filename);
+               output(opt, 2, _("Auto-merging %s"), filename);
 
        return 0;
 }
 
-static int handle_rename_via_dir(struct merge_options *o,
-                                struct diff_filepair *pair,
-                                const char *rename_branch)
+static int handle_rename_via_dir(struct merge_options *opt,
+                                struct rename_conflict_info *ci)
 {
        /*
         * Handle file adds that need to be renamed due to directory rename
@@ -1410,38 +1373,47 @@ static int handle_rename_via_dir(struct merge_options *o,
         * there is no content merge to do; just move the file into the
         * desired final location.
         */
-       const struct diff_filespec *dest = pair->two;
+       const struct rename *ren = ci->ren1;
+       const struct diff_filespec *dest = ren->pair->two;
+       char *file_path = dest->path;
+       int mark_conflicted = (opt->detect_directory_renames == 1);
+       assert(ren->dir_rename_original_dest);
 
-       if (!o->call_depth && would_lose_untracked(o, dest->path)) {
-               char *alt_path = unique_path(o, dest->path, rename_branch);
+       if (!opt->call_depth && would_lose_untracked(opt, dest->path)) {
+               mark_conflicted = 1;
+               file_path = unique_path(opt, dest->path, ren->branch);
+               output(opt, 1, _("Error: Refusing to lose untracked file at %s; "
+                                "writing to %s instead."),
+                      dest->path, file_path);
+       }
 
-               output(o, 1, _("Error: Refusing to lose untracked file at %s; "
-                              "writing to %s instead."),
-                      dest->path, alt_path);
+       if (mark_conflicted) {
                /*
-                * Write the file in worktree at alt_path, but not in the
-                * index.  Instead, write to dest->path for the index but
-                * only at the higher appropriate stage.
+                * Write the file in worktree at file_path.  In the index,
+                * only record the file at dest->path in the appropriate
+                * higher stage.
                 */
-               if (update_file(o, 0, &dest->oid, dest->mode, alt_path))
+               if (update_file(opt, 0, dest, file_path))
+                       return -1;
+               if (file_path != dest->path)
+                       free(file_path);
+               if (update_stages(opt, dest->path, NULL,
+                                 ren->branch == opt->branch1 ? dest : NULL,
+                                 ren->branch == opt->branch1 ? NULL : dest))
                        return -1;
-               free(alt_path);
-               return update_stages(o, dest->path, NULL,
-                                    rename_branch == o->branch1 ? dest : NULL,
-                                    rename_branch == o->branch1 ? NULL : dest);
+               return 0; /* not clean, but conflicted */
+       } else {
+               /* Update dest->path both in index and in worktree */
+               if (update_file(opt, 1, dest, dest->path))
+                       return -1;
+               return 1; /* clean */
        }
-
-       /* Update dest->path both in index and in worktree */
-       if (update_file(o, 1, &dest->oid, dest->mode, dest->path))
-               return -1;
-       return 0;
 }
 
-static int handle_change_delete(struct merge_options *o,
+static int handle_change_delete(struct merge_options *opt,
                                const char *path, const char *old_path,
-                               const struct object_id *o_oid, int o_mode,
-                               const struct object_id *changed_oid,
-                               int changed_mode,
+                               const struct diff_filespec *o,
+                               const struct diff_filespec *changed,
                                const char *change_branch,
                                const char *delete_branch,
                                const char *change, const char *change_past)
@@ -1450,20 +1422,20 @@ static int handle_change_delete(struct merge_options *o,
        const char *update_path = path;
        int ret = 0;
 
-       if (dir_in_way(o->repo->index, path, !o->call_depth, 0) ||
-           (!o->call_depth && would_lose_untracked(o, path))) {
-               update_path = alt_path = unique_path(o, path, change_branch);
+       if (dir_in_way(opt->repo->index, path, !opt->call_depth, 0) ||
+           (!opt->call_depth && would_lose_untracked(opt, path))) {
+               update_path = alt_path = unique_path(opt, path, change_branch);
        }
 
-       if (o->call_depth) {
+       if (opt->call_depth) {
                /*
                 * We cannot arbitrarily accept either a_sha or b_sha as
                 * correct; since there is no true "middle point" between
                 * them, simply reuse the base version for virtual merge base.
                 */
-               ret = remove_file_from_index(o->repo->index, path);
+               ret = remove_file_from_index(opt->repo->index, path);
                if (!ret)
-                       ret = update_file(o, 0, o_oid, o_mode, update_path);
+                       ret = update_file(opt, 0, o, update_path);
        } else {
                /*
                 * Despite the four nearly duplicate messages and argument
@@ -1482,24 +1454,24 @@ static int handle_change_delete(struct merge_options *o,
                 */
                if (!alt_path) {
                        if (!old_path) {
-                               output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+                               output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
                                       "and %s in %s. Version %s of %s left in tree."),
                                       change, path, delete_branch, change_past,
                                       change_branch, change_branch, path);
                        } else {
-                               output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+                               output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
                                       "and %s to %s in %s. Version %s of %s left in tree."),
                                       change, old_path, delete_branch, change_past, path,
                                       change_branch, change_branch, path);
                        }
                } else {
                        if (!old_path) {
-                               output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+                               output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
                                       "and %s in %s. Version %s of %s left in tree at %s."),
                                       change, path, delete_branch, change_past,
                                       change_branch, change_branch, path, alt_path);
                        } else {
-                               output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+                               output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
                                       "and %s to %s in %s. Version %s of %s left in tree at %s."),
                                       change, old_path, delete_branch, change_past, path,
                                       change_branch, change_branch, path, alt_path);
@@ -1507,142 +1479,127 @@ static int handle_change_delete(struct merge_options *o,
                }
                /*
                 * No need to call update_file() on path when change_branch ==
-                * o->branch1 && !alt_path, since that would needlessly touch
+                * opt->branch1 && !alt_path, since that would needlessly touch
                 * path.  We could call update_file_flags() with update_cache=0
                 * and update_wd=0, but that's a no-op.
                 */
-               if (change_branch != o->branch1 || alt_path)
-                       ret = update_file(o, 0, changed_oid, changed_mode, update_path);
+               if (change_branch != opt->branch1 || alt_path)
+                       ret = update_file(opt, 0, changed, update_path);
        }
        free(alt_path);
 
        return ret;
 }
 
-static int handle_rename_delete(struct merge_options *o,
-                               struct diff_filepair *pair,
-                               const char *rename_branch,
-                               const char *delete_branch)
+static int handle_rename_delete(struct merge_options *opt,
+                               struct rename_conflict_info *ci)
 {
-       const struct diff_filespec *orig = pair->one;
-       const struct diff_filespec *dest = pair->two;
-
-       if (handle_change_delete(o,
-                                o->call_depth ? orig->path : dest->path,
-                                o->call_depth ? NULL : orig->path,
-                                &orig->oid, orig->mode,
-                                &dest->oid, dest->mode,
+       const struct rename *ren = ci->ren1;
+       const struct diff_filespec *orig = ren->pair->one;
+       const struct diff_filespec *dest = ren->pair->two;
+       const char *rename_branch = ren->branch;
+       const char *delete_branch = (opt->branch1 == ren->branch ?
+                                    opt->branch2 : opt->branch1);
+
+       if (handle_change_delete(opt,
+                                opt->call_depth ? orig->path : dest->path,
+                                opt->call_depth ? NULL : orig->path,
+                                orig, dest,
                                 rename_branch, delete_branch,
                                 _("rename"), _("renamed")))
                return -1;
 
-       if (o->call_depth)
-               return remove_file_from_index(o->repo->index, dest->path);
+       if (opt->call_depth)
+               return remove_file_from_index(opt->repo->index, dest->path);
        else
-               return update_stages(o, dest->path, NULL,
-                                    rename_branch == o->branch1 ? dest : NULL,
-                                    rename_branch == o->branch1 ? NULL : dest);
+               return update_stages(opt, dest->path, NULL,
+                                    rename_branch == opt->branch1 ? dest : NULL,
+                                    rename_branch == opt->branch1 ? NULL : dest);
 }
 
-static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,
-                                                struct stage_data *entry,
-                                                int stage)
-{
-       struct object_id *oid = &entry->stages[stage].oid;
-       unsigned mode = entry->stages[stage].mode;
-       if (mode == 0 || is_null_oid(oid))
-               return NULL;
-       oidcpy(&target->oid, oid);
-       target->mode = mode;
-       return target;
-}
-
-static int handle_file_collision(struct merge_options *o,
+static int handle_file_collision(struct merge_options *opt,
                                 const char *collide_path,
                                 const char *prev_path1,
                                 const char *prev_path2,
                                 const char *branch1, const char *branch2,
-                                const struct object_id *a_oid,
-                                unsigned int a_mode,
-                                const struct object_id *b_oid,
-                                unsigned int b_mode)
+                                struct diff_filespec *a,
+                                struct diff_filespec *b)
 {
        struct merge_file_info mfi;
-       struct diff_filespec null, a, b;
+       struct diff_filespec null;
        char *alt_path = NULL;
        const char *update_path = collide_path;
 
        /*
         * 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
+        * branch if we just ensure that branch1 == opt->branch1.  So, simply
         * flip arguments around if we don't have that.
         */
-       if (branch1 != o->branch1) {
-               return handle_file_collision(o, collide_path,
+       if (branch1 != opt->branch1) {
+               return handle_file_collision(opt, collide_path,
                                             prev_path2, prev_path1,
                                             branch2, branch1,
-                                            b_oid, b_mode,
-                                            a_oid, a_mode);
+                                            b, a);
        }
 
        /*
         * In the recursive case, we just opt to undo renames
         */
-       if (o->call_depth && (prev_path1 || prev_path2)) {
-               /* Put first file (a_oid, a_mode) in its original spot */
+       if (opt->call_depth && (prev_path1 || prev_path2)) {
+               /* Put first file (a->oid, a->mode) in its original spot */
                if (prev_path1) {
-                       if (update_file(o, 1, a_oid, a_mode, prev_path1))
+                       if (update_file(opt, 1, a, prev_path1))
                                return -1;
                } else {
-                       if (update_file(o, 1, a_oid, a_mode, collide_path))
+                       if (update_file(opt, 1, a, collide_path))
                                return -1;
                }
 
-               /* Put second file (b_oid, b_mode) in its original spot */
+               /* Put second file (b->oid, b->mode) in its original spot */
                if (prev_path2) {
-                       if (update_file(o, 1, b_oid, b_mode, prev_path2))
+                       if (update_file(opt, 1, b, prev_path2))
                                return -1;
                } else {
-                       if (update_file(o, 1, b_oid, b_mode, collide_path))
+                       if (update_file(opt, 1, b, collide_path))
                                return -1;
                }
 
                /* Don't leave something at collision path if unrenaming both */
                if (prev_path1 && prev_path2)
-                       remove_file(o, 1, collide_path, 0);
+                       remove_file(opt, 1, collide_path, 0);
 
                return 0;
        }
 
        /* Remove rename sources if rename/add or rename/rename(2to1) */
        if (prev_path1)
-               remove_file(o, 1, prev_path1,
-                           o->call_depth || would_lose_untracked(o, prev_path1));
+               remove_file(opt, 1, prev_path1,
+                           opt->call_depth || would_lose_untracked(opt, prev_path1));
        if (prev_path2)
-               remove_file(o, 1, prev_path2,
-                           o->call_depth || would_lose_untracked(o, prev_path2));
+               remove_file(opt, 1, prev_path2,
+                           opt->call_depth || would_lose_untracked(opt, prev_path2));
 
        /*
         * Remove the collision path, if it wouldn't cause dirty contents
         * or an untracked file to get lost.  We'll either overwrite with
         * merged contents, or just write out to differently named files.
         */
-       if (was_dirty(o, collide_path)) {
-               output(o, 1, _("Refusing to lose dirty file at %s"),
+       if (was_dirty(opt, collide_path)) {
+               output(opt, 1, _("Refusing to lose dirty file at %s"),
                       collide_path);
-               update_path = alt_path = unique_path(o, collide_path, "merged");
-       } else if (would_lose_untracked(o, collide_path)) {
+               update_path = alt_path = unique_path(opt, collide_path, "merged");
+       } else if (would_lose_untracked(opt, collide_path)) {
                /*
                 * Only way we get here is if both renames were from
                 * a directory rename AND user had an untracked file
                 * at the location where both files end up after the
                 * two directory renames.  See testcase 10d of t6043.
                 */
-               output(o, 1, _("Refusing to lose untracked file at "
+               output(opt, 1, _("Refusing to lose untracked file at "
                               "%s, even though it's in the way."),
                       collide_path);
-               update_path = alt_path = unique_path(o, collide_path, "merged");
+               update_path = alt_path = unique_path(opt, collide_path, "merged");
        } else {
                /*
                 * FIXME: It's possible that the two files are identical
@@ -1654,30 +1611,22 @@ static int handle_file_collision(struct merge_options *o,
                 * merge-recursive interoperate anyway, so punting for
                 * now...
                 */
-               remove_file(o, 0, collide_path, 0);
+               remove_file(opt, 0, collide_path, 0);
        }
 
        /* Store things in diff_filespecs for functions that need it */
-       memset(&a, 0, sizeof(struct diff_filespec));
-       memset(&b, 0, sizeof(struct diff_filespec));
-       null.path = a.path = b.path = (char *)collide_path;
+       null.path = (char *)collide_path;
        oidcpy(&null.oid, &null_oid);
        null.mode = 0;
-       oidcpy(&a.oid, a_oid);
-       a.mode = a_mode;
-       a.oid_valid = 1;
-       oidcpy(&b.oid, b_oid);
-       b.mode = b_mode;
-       b.oid_valid = 1;
-
-       if (merge_mode_and_contents(o, &null, &a, &b, collide_path,
-                                   branch1, branch2, o->call_depth * 2, &mfi))
+
+       if (merge_mode_and_contents(opt, &null, a, b, collide_path,
+                                   branch1, branch2, opt->call_depth * 2, &mfi))
                return -1;
        mfi.clean &= !alt_path;
-       if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, update_path))
+       if (update_file(opt, mfi.clean, &mfi.blob, update_path))
                return -1;
-       if (!mfi.clean && !o->call_depth &&
-           update_stages(o, collide_path, NULL, &a, &b))
+       if (!mfi.clean && !opt->call_depth &&
+           update_stages(opt, collide_path, NULL, a, b))
                return -1;
        free(alt_path);
        /*
@@ -1690,52 +1639,57 @@ static int handle_file_collision(struct merge_options *o,
        return mfi.clean;
 }
 
-static int handle_rename_add(struct merge_options *o,
+static int handle_rename_add(struct merge_options *opt,
                             struct rename_conflict_info *ci)
 {
        /* a was renamed to c, and a separate c was added. */
-       struct diff_filespec *a = ci->pair1->one;
-       struct diff_filespec *c = ci->pair1->two;
+       struct diff_filespec *a = ci->ren1->pair->one;
+       struct diff_filespec *c = ci->ren1->pair->two;
        char *path = c->path;
        char *prev_path_desc;
        struct merge_file_info mfi;
 
-       int other_stage = (ci->branch1 == o->branch1 ? 3 : 2);
+       const char *rename_branch = ci->ren1->branch;
+       const char *add_branch = (opt->branch1 == rename_branch ?
+                                 opt->branch2 : opt->branch1);
+       int other_stage = (ci->ren1->branch == opt->branch1 ? 3 : 2);
 
-       output(o, 1, _("CONFLICT (rename/add): "
+       output(opt, 1, _("CONFLICT (rename/add): "
               "Rename %s->%s in %s.  Added %s in %s"),
-              a->path, c->path, ci->branch1,
-              c->path, ci->branch2);
+              a->path, c->path, rename_branch,
+              c->path, add_branch);
 
        prev_path_desc = xstrfmt("version of %s from %s", path, a->path);
-       if (merge_mode_and_contents(o, a, c, &ci->ren1_other, prev_path_desc,
-                                   o->branch1, o->branch2,
-                                   1 + o->call_depth * 2, &mfi))
+       if (merge_mode_and_contents(opt, a, c,
+                                   &ci->ren1->src_entry->stages[other_stage],
+                                   prev_path_desc,
+                                   opt->branch1, opt->branch2,
+                                   1 + opt->call_depth * 2, &mfi))
                return -1;
        free(prev_path_desc);
 
-       return handle_file_collision(o,
+       ci->ren1->dst_entry->stages[other_stage].path = mfi.blob.path = c->path;
+       return handle_file_collision(opt,
                                     c->path, a->path, NULL,
-                                    ci->branch1, ci->branch2,
-                                    &mfi.oid, mfi.mode,
-                                    &ci->dst_entry1->stages[other_stage].oid,
-                                    ci->dst_entry1->stages[other_stage].mode);
+                                    rename_branch, add_branch,
+                                    &mfi.blob,
+                                    &ci->ren1->dst_entry->stages[other_stage]);
 }
 
-static char *find_path_for_conflict(struct merge_options *o,
+static char *find_path_for_conflict(struct merge_options *opt,
                                    const char *path,
                                    const char *branch1,
                                    const char *branch2)
 {
        char *new_path = NULL;
-       if (dir_in_way(o->repo->index, path, !o->call_depth, 0)) {
-               new_path = unique_path(o, path, branch1);
-               output(o, 1, _("%s is a directory in %s adding "
+       if (dir_in_way(opt->repo->index, path, !opt->call_depth, 0)) {
+               new_path = unique_path(opt, path, branch1);
+               output(opt, 1, _("%s is a directory in %s adding "
                               "as %s instead"),
                       path, branch2, new_path);
-       } else if (would_lose_untracked(o, path)) {
-               new_path = unique_path(o, path, branch1);
-               output(o, 1, _("Refusing to lose untracked file"
+       } else if (would_lose_untracked(opt, path)) {
+               new_path = unique_path(opt, path, branch1);
+               output(opt, 1, _("Refusing to lose untracked file"
                               " at %s; adding as %s instead"),
                       path, new_path);
        }
@@ -1743,41 +1697,40 @@ static char *find_path_for_conflict(struct merge_options *o,
        return new_path;
 }
 
-static int handle_rename_rename_1to2(struct merge_options *o,
+static int handle_rename_rename_1to2(struct merge_options *opt,
                                     struct rename_conflict_info *ci)
 {
        /* One file was renamed in both branches, but to different names. */
        struct merge_file_info mfi;
-       struct diff_filespec other;
        struct diff_filespec *add;
-       struct diff_filespec *one = ci->pair1->one;
-       struct diff_filespec *a = ci->pair1->two;
-       struct diff_filespec *b = ci->pair2->two;
+       struct diff_filespec *o = ci->ren1->pair->one;
+       struct diff_filespec *a = ci->ren1->pair->two;
+       struct diff_filespec *b = ci->ren2->pair->two;
        char *path_desc;
 
-       output(o, 1, _("CONFLICT (rename/rename): "
+       output(opt, 1, _("CONFLICT (rename/rename): "
               "Rename \"%s\"->\"%s\" in branch \"%s\" "
               "rename \"%s\"->\"%s\" in \"%s\"%s"),
-              one->path, a->path, ci->branch1,
-              one->path, b->path, ci->branch2,
-              o->call_depth ? _(" (left unresolved)") : "");
+              o->path, a->path, ci->ren1->branch,
+              o->path, b->path, ci->ren2->branch,
+              opt->call_depth ? _(" (left unresolved)") : "");
 
        path_desc = xstrfmt("%s and %s, both renamed from %s",
-                           a->path, b->path, one->path);
-       if (merge_mode_and_contents(o, one, a, b, path_desc,
-                                   ci->branch1, ci->branch2,
-                                   o->call_depth * 2, &mfi))
+                           a->path, b->path, o->path);
+       if (merge_mode_and_contents(opt, o, a, b, path_desc,
+                                   ci->ren1->branch, ci->ren2->branch,
+                                   opt->call_depth * 2, &mfi))
                return -1;
        free(path_desc);
 
-       if (o->call_depth) {
+       if (opt->call_depth) {
                /*
                 * FIXME: For rename/add-source conflicts (if we could detect
                 * such), this is wrong.  We should instead find a unique
                 * pathname and then either rename the add-source file to that
                 * unique path, or use that unique path instead of src here.
                 */
-               if (update_file(o, 0, &mfi.oid, mfi.mode, one->path))
+               if (update_file(opt, 0, &mfi.blob, o->path))
                        return -1;
 
                /*
@@ -1788,61 +1741,65 @@ static int handle_rename_rename_1to2(struct merge_options *o,
                 * such cases, we should keep the added file around,
                 * resolving the conflict at that path in its favor.
                 */
-               add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);
-               if (add) {
-                       if (update_file(o, 0, &add->oid, add->mode, a->path))
+               add = &ci->ren1->dst_entry->stages[2 ^ 1];
+               if (is_valid(add)) {
+                       if (update_file(opt, 0, add, a->path))
                                return -1;
                }
                else
-                       remove_file_from_index(o->repo->index, a->path);
-               add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);
-               if (add) {
-                       if (update_file(o, 0, &add->oid, add->mode, b->path))
+                       remove_file_from_index(opt->repo->index, a->path);
+               add = &ci->ren2->dst_entry->stages[3 ^ 1];
+               if (is_valid(add)) {
+                       if (update_file(opt, 0, add, b->path))
                                return -1;
                }
                else
-                       remove_file_from_index(o->repo->index, b->path);
+                       remove_file_from_index(opt->repo->index, b->path);
        } else {
                /*
                 * For each destination path, we need to see if there is a
                 * rename/add collision.  If not, we can write the file out
                 * to the specified location.
                 */
-               add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);
-               if (add) {
-                       if (handle_file_collision(o, a->path,
+               add = &ci->ren1->dst_entry->stages[2 ^ 1];
+               if (is_valid(add)) {
+                       add->path = mfi.blob.path = a->path;
+                       if (handle_file_collision(opt, a->path,
                                                  NULL, NULL,
-                                                 ci->branch1, ci->branch2,
-                                                 &mfi.oid, mfi.mode,
-                                                 &add->oid, add->mode) < 0)
+                                                 ci->ren1->branch,
+                                                 ci->ren2->branch,
+                                                 &mfi.blob, add) < 0)
                                return -1;
                } else {
-                       char *new_path = find_path_for_conflict(o, a->path,
-                                                               ci->branch1,
-                                                               ci->branch2);
-                       if (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))
+                       char *new_path = find_path_for_conflict(opt, a->path,
+                                                               ci->ren1->branch,
+                                                               ci->ren2->branch);
+                       if (update_file(opt, 0, &mfi.blob,
+                                       new_path ? new_path : a->path))
                                return -1;
                        free(new_path);
-                       if (update_stages(o, a->path, NULL, a, NULL))
+                       if (update_stages(opt, a->path, NULL, a, NULL))
                                return -1;
                }
 
-               add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);
-               if (add) {
-                       if (handle_file_collision(o, b->path,
+               add = &ci->ren2->dst_entry->stages[3 ^ 1];
+               if (is_valid(add)) {
+                       add->path = mfi.blob.path = b->path;
+                       if (handle_file_collision(opt, b->path,
                                                  NULL, NULL,
-                                                 ci->branch1, ci->branch2,
-                                                 &add->oid, add->mode,
-                                                 &mfi.oid, mfi.mode) < 0)
+                                                 ci->ren1->branch,
+                                                 ci->ren2->branch,
+                                                 add, &mfi.blob) < 0)
                                return -1;
                } else {
-                       char *new_path = find_path_for_conflict(o, b->path,
-                                                               ci->branch2,
-                                                               ci->branch1);
-                       if (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))
+                       char *new_path = find_path_for_conflict(opt, b->path,
+                                                               ci->ren2->branch,
+                                                               ci->ren1->branch);
+                       if (update_file(opt, 0, &mfi.blob,
+                                       new_path ? new_path : b->path))
                                return -1;
                        free(new_path);
-                       if (update_stages(o, b->path, NULL, NULL, b))
+                       if (update_stages(opt, b->path, NULL, NULL, b))
                                return -1;
                }
        }
@@ -1850,58 +1807,68 @@ static int handle_rename_rename_1to2(struct merge_options *o,
        return 0;
 }
 
-static int handle_rename_rename_2to1(struct merge_options *o,
+static int handle_rename_rename_2to1(struct merge_options *opt,
                                     struct rename_conflict_info *ci)
 {
        /* Two files, a & b, were renamed to the same thing, c. */
-       struct diff_filespec *a = ci->pair1->one;
-       struct diff_filespec *b = ci->pair2->one;
-       struct diff_filespec *c1 = ci->pair1->two;
-       struct diff_filespec *c2 = ci->pair2->two;
+       struct diff_filespec *a = ci->ren1->pair->one;
+       struct diff_filespec *b = ci->ren2->pair->one;
+       struct diff_filespec *c1 = ci->ren1->pair->two;
+       struct diff_filespec *c2 = ci->ren2->pair->two;
        char *path = c1->path; /* == c2->path */
        char *path_side_1_desc;
        char *path_side_2_desc;
        struct merge_file_info mfi_c1;
        struct merge_file_info mfi_c2;
+       int ostage1, ostage2;
 
-       output(o, 1, _("CONFLICT (rename/rename): "
+       output(opt, 1, _("CONFLICT (rename/rename): "
               "Rename %s->%s in %s. "
               "Rename %s->%s in %s"),
-              a->path, c1->path, ci->branch1,
-              b->path, c2->path, ci->branch2);
+              a->path, c1->path, ci->ren1->branch,
+              b->path, c2->path, ci->ren2->branch);
 
        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,
-                                   1 + o->call_depth * 2, &mfi_c1) ||
-           merge_mode_and_contents(o, b, &ci->ren2_other, c2, path_side_2_desc,
-                                   o->branch1, o->branch2,
-                                   1 + o->call_depth * 2, &mfi_c2))
+       ostage1 = ci->ren1->branch == opt->branch1 ? 3 : 2;
+       ostage2 = ostage1 ^ 1;
+       ci->ren1->src_entry->stages[ostage1].path = a->path;
+       ci->ren2->src_entry->stages[ostage2].path = b->path;
+       if (merge_mode_and_contents(opt, a, c1,
+                                   &ci->ren1->src_entry->stages[ostage1],
+                                   path_side_1_desc,
+                                   opt->branch1, opt->branch2,
+                                   1 + opt->call_depth * 2, &mfi_c1) ||
+           merge_mode_and_contents(opt, b,
+                                   &ci->ren2->src_entry->stages[ostage2],
+                                   c2, path_side_2_desc,
+                                   opt->branch1, opt->branch2,
+                                   1 + opt->call_depth * 2, &mfi_c2))
                return -1;
        free(path_side_1_desc);
        free(path_side_2_desc);
+       mfi_c1.blob.path = path;
+       mfi_c2.blob.path = path;
 
-       return handle_file_collision(o, path, a->path, b->path,
-                                    ci->branch1, ci->branch2,
-                                    &mfi_c1.oid, mfi_c1.mode,
-                                    &mfi_c2.oid, mfi_c2.mode);
+       return handle_file_collision(opt, path, a->path, b->path,
+                                    ci->ren1->branch, ci->ren2->branch,
+                                    &mfi_c1.blob, &mfi_c2.blob);
 }
 
 /*
  * Get the diff_filepairs changed between o_tree and tree.
  */
-static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
+static struct diff_queue_struct *get_diffpairs(struct merge_options *opt,
                                               struct tree *o_tree,
                                               struct tree *tree)
 {
        struct diff_queue_struct *ret;
        struct diff_options opts;
 
-       repo_diff_setup(o->repo, &opts);
+       repo_diff_setup(opt->repo, &opts);
        opts.flags.recursive = 1;
        opts.flags.rename_empty = 0;
-       opts.detect_rename = merge_detect_rename(o);
+       opts.detect_rename = merge_detect_rename(opt);
        /*
         * We do not have logic to handle the detection of copies.  In
         * fact, it may not even make sense to add such logic: would we
@@ -1910,17 +1877,17 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
         */
        if (opts.detect_rename > DIFF_DETECT_RENAME)
                opts.detect_rename = DIFF_DETECT_RENAME;
-       opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
-                           o->diff_rename_limit >= 0 ? o->diff_rename_limit :
+       opts.rename_limit = opt->merge_rename_limit >= 0 ? opt->merge_rename_limit :
+                           opt->diff_rename_limit >= 0 ? opt->diff_rename_limit :
                            1000;
-       opts.rename_score = o->rename_score;
-       opts.show_rename_progress = o->show_rename_progress;
+       opts.rename_score = opt->rename_score;
+       opts.show_rename_progress = opt->show_rename_progress;
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&opts);
        diff_tree_oid(&o_tree->object.oid, &tree->object.oid, "", &opts);
        diffcore_std(&opts);
-       if (opts.needed_rename_limit > o->needed_rename_limit)
-               o->needed_rename_limit = opts.needed_rename_limit;
+       if (opts.needed_rename_limit > opt->needed_rename_limit)
+               opt->needed_rename_limit = opts.needed_rename_limit;
 
        ret = xmalloc(sizeof(*ret));
        *ret = diff_queued_diff;
@@ -1935,7 +1902,7 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
 static int tree_has_path(struct tree *tree, const char *path)
 {
        struct object_id hashy;
-       unsigned int mode_o;
+       unsigned short mode_o;
 
        return !get_tree_entry(&tree->object.oid, path,
                               &hashy, &mode_o);
@@ -2039,7 +2006,7 @@ static void remove_hashmap_entries(struct hashmap *dir_renames,
  * level conflicts for the renamed location.  If there is a rename and
  * there are no conflicts, return the new name.  Otherwise, return NULL.
  */
-static char *handle_path_level_conflicts(struct merge_options *o,
+static char *handle_path_level_conflicts(struct merge_options *opt,
                                         const char *path,
                                         struct dir_rename_entry *entry,
                                         struct hashmap *collisions,
@@ -2060,7 +2027,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,
                /* This should only happen when entry->non_unique_new_dir set */
                if (!entry->non_unique_new_dir)
                        BUG("entry->non_unqiue_dir not set and !new_path");
-               output(o, 1, _("CONFLICT (directory rename split): "
+               output(opt, 1, _("CONFLICT (directory rename split): "
                               "Unclear where to place %s because directory "
                               "%s was renamed to multiple other directories, "
                               "with no destination getting a majority of the "
@@ -2092,7 +2059,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,
                collision_ent->reported_already = 1;
                strbuf_add_separated_string_list(&collision_paths, ", ",
                                                 &collision_ent->source_files);
-               output(o, 1, _("CONFLICT (implicit dir rename): Existing "
+               output(opt, 1, _("CONFLICT (implicit dir rename): Existing "
                               "file/dir at %s in the way of implicit "
                               "directory rename(s) putting the following "
                               "path(s) there: %s."),
@@ -2102,7 +2069,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,
                collision_ent->reported_already = 1;
                strbuf_add_separated_string_list(&collision_paths, ", ",
                                                 &collision_ent->source_files);
-               output(o, 1, _("CONFLICT (implicit dir rename): Cannot map "
+               output(opt, 1, _("CONFLICT (implicit dir rename): Cannot map "
                               "more than one path to %s; implicit directory "
                               "renames tried to put these paths there: %s"),
                       new_path, collision_paths.buf);
@@ -2139,7 +2106,7 @@ static char *handle_path_level_conflicts(struct merge_options *o,
  *         causes conflicts for files within those merged directories, then
  *         that should be detected at the individual path level.
  */
-static void handle_directory_level_conflicts(struct merge_options *o,
+static void handle_directory_level_conflicts(struct merge_options *opt,
                                             struct hashmap *dir_re_head,
                                             struct tree *head,
                                             struct hashmap *dir_re_merge,
@@ -2194,11 +2161,11 @@ static void handle_directory_level_conflicts(struct merge_options *o,
                         * know that head_ent->new_dir and merge_ent->new_dir
                         * are different strings.
                         */
-                       output(o, 1, _("CONFLICT (rename/rename): "
+                       output(opt, 1, _("CONFLICT (rename/rename): "
                                       "Rename directory %s->%s in %s. "
                                       "Rename directory %s->%s in %s"),
-                              head_ent->dir, head_ent->new_dir.buf, o->branch1,
-                              head_ent->dir, merge_ent->new_dir.buf, o->branch2);
+                              head_ent->dir, head_ent->new_dir.buf, opt->branch1,
+                              head_ent->dir, merge_ent->new_dir.buf, opt->branch2);
                        string_list_append(&remove_from_head,
                                           head_ent->dir)->util = head_ent;
                        strbuf_release(&head_ent->new_dir);
@@ -2397,7 +2364,7 @@ static void compute_collisions(struct hashmap *collisions,
        }
 }
 
-static char *check_for_directory_rename(struct merge_options *o,
+static char *check_for_directory_rename(struct merge_options *opt,
                                        const char *path,
                                        struct tree *tree,
                                        struct hashmap *dir_renames,
@@ -2438,11 +2405,11 @@ static char *check_for_directory_rename(struct merge_options *o,
         */
        oentry = dir_rename_find_entry(dir_rename_exclusions, entry->new_dir.buf);
        if (oentry) {
-               output(o, 1, _("WARNING: Avoiding applying %s -> %s rename "
+               output(opt, 1, _("WARNING: Avoiding applying %s -> %s rename "
                               "to %s, because %s itself was renamed."),
                       entry->dir, entry->new_dir.buf, path, entry->new_dir.buf);
        } else {
-               new_path = handle_path_level_conflicts(o, path, entry,
+               new_path = handle_path_level_conflicts(opt, path, entry,
                                                       collisions, tree);
                *clean_merge &= (new_path != NULL);
        }
@@ -2450,7 +2417,7 @@ static char *check_for_directory_rename(struct merge_options *o,
        return new_path;
 }
 
-static void apply_directory_rename_modifications(struct merge_options *o,
+static void apply_directory_rename_modifications(struct merge_options *opt,
                                                 struct diff_filepair *pair,
                                                 char *new_path,
                                                 struct rename *re,
@@ -2473,11 +2440,11 @@ static void apply_directory_rename_modifications(struct merge_options *o,
         * saying the file would have been overwritten), but it might
         * be dirty, though.
         */
-       update_wd = !was_dirty(o, pair->two->path);
+       update_wd = !was_dirty(opt, pair->two->path);
        if (!update_wd)
-               output(o, 1, _("Refusing to lose dirty file at %s"),
+               output(opt, 1, _("Refusing to lose dirty file at %s"),
                       pair->two->path);
-       remove_file(o, 1, pair->two->path, !update_wd);
+       remove_file(opt, 1, pair->two->path, !update_wd);
 
        /* Find or create a new re->dst_entry */
        item = string_list_lookup(entries, new_path);
@@ -2537,16 +2504,18 @@ static void apply_directory_rename_modifications(struct merge_options *o,
                       &re->dst_entry->stages[stage].oid,
                       &re->dst_entry->stages[stage].mode);
 
-       /* Update pair status */
-       if (pair->status == 'A') {
-               /*
-                * Recording rename information for this add makes it look
-                * like a rename/delete conflict.  Make sure we can
-                * correctly handle this as an add that was moved to a new
-                * directory instead of reporting a rename/delete conflict.
-                */
-               re->add_turned_into_rename = 1;
-       }
+       /*
+        * Record the original change status (or 'type' of change).  If it
+        * was originally an add ('A'), this lets us differentiate later
+        * between a RENAME_DELETE conflict and RENAME_VIA_DIR (they
+        * otherwise look the same).  If it was originally a rename ('R'),
+        * this lets us remember and report accurately about the transitive
+        * renaming that occurred via the directory rename detection.  Also,
+        * record the original destination name.
+        */
+       re->dir_rename_original_type = pair->status;
+       re->dir_rename_original_dest = pair->two->path;
+
        /*
         * We don't actually look at pair->status again, but it seems
         * pedagogically correct to adjust it.
@@ -2566,7 +2535,8 @@ static void apply_directory_rename_modifications(struct merge_options *o,
  * to be able to associate the correct cache entries with the rename
  * information; tree is always equal to either a_tree or b_tree.
  */
-static struct string_list *get_renames(struct merge_options *o,
+static struct string_list *get_renames(struct merge_options *opt,
+                                      const char *branch,
                                       struct diff_queue_struct *pairs,
                                       struct hashmap *dir_renames,
                                       struct hashmap *dir_rename_exclusions,
@@ -2596,7 +2566,7 @@ static struct string_list *get_renames(struct merge_options *o,
                        diff_free_filepair(pair);
                        continue;
                }
-               new_path = check_for_directory_rename(o, pair->two->path, tree,
+               new_path = check_for_directory_rename(opt, pair->two->path, tree,
                                                      dir_renames,
                                                      dir_rename_exclusions,
                                                      &collisions,
@@ -2608,8 +2578,10 @@ static struct string_list *get_renames(struct merge_options *o,
 
                re = xmalloc(sizeof(*re));
                re->processed = 0;
-               re->add_turned_into_rename = 0;
                re->pair = pair;
+               re->branch = branch;
+               re->dir_rename_original_type = '\0';
+               re->dir_rename_original_dest = NULL;
                item = string_list_lookup(entries, re->pair->one->path);
                if (!item)
                        re->src_entry = insert_stage_data(re->pair->one->path,
@@ -2626,7 +2598,7 @@ static struct string_list *get_renames(struct merge_options *o,
                item = string_list_insert(renames, pair->one->path);
                item->util = re;
                if (new_path)
-                       apply_directory_rename_modifications(o, pair, new_path,
+                       apply_directory_rename_modifications(opt, pair, new_path,
                                                             re, tree, o_tree,
                                                             a_tree, b_tree,
                                                             entries);
@@ -2641,7 +2613,7 @@ static struct string_list *get_renames(struct merge_options *o,
        return renames;
 }
 
-static int process_renames(struct merge_options *o,
+static int process_renames(struct merge_options *opt,
                           struct string_list *a_renames,
                           struct string_list *b_renames)
 {
@@ -2664,7 +2636,6 @@ static int process_renames(struct merge_options *o,
        for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
                struct string_list *renames1, *renames2Dst;
                struct rename *ren1 = NULL, *ren2 = NULL;
-               const char *branch1, *branch2;
                const char *ren1_src, *ren1_dst;
                struct string_list_item *lookup;
 
@@ -2685,13 +2656,9 @@ static int process_renames(struct merge_options *o,
                if (ren1) {
                        renames1 = a_renames;
                        renames2Dst = &b_by_dst;
-                       branch1 = o->branch1;
-                       branch2 = o->branch2;
                } else {
                        renames1 = b_renames;
                        renames2Dst = &a_by_dst;
-                       branch1 = o->branch2;
-                       branch2 = o->branch1;
                        SWAP(ren2, ren1);
                }
 
@@ -2725,22 +2692,13 @@ static int process_renames(struct merge_options *o,
                                 * the base stage (think of rename +
                                 * add-source cases).
                                 */
-                               remove_file(o, 1, ren1_src, 1);
+                               remove_file(opt, 1, ren1_src, 1);
                                update_entry(ren1->dst_entry,
                                             ren1->pair->one,
                                             ren1->pair->two,
                                             ren2->pair->two);
                        }
-                       setup_rename_conflict_info(rename_type,
-                                                  ren1->pair,
-                                                  ren2->pair,
-                                                  branch1,
-                                                  branch2,
-                                                  ren1->dst_entry,
-                                                  ren2->dst_entry,
-                                                  o,
-                                                  NULL,
-                                                  NULL);
+                       setup_rename_conflict_info(rename_type, opt, ren1, ren2);
                } else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {
                        /* Two different files renamed to the same thing */
                        char *ren2_dst;
@@ -2759,16 +2717,7 @@ static int process_renames(struct merge_options *o,
                        ren2->src_entry->processed = 1;
 
                        setup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,
-                                                  ren1->pair,
-                                                  ren2->pair,
-                                                  branch1,
-                                                  branch2,
-                                                  ren1->dst_entry,
-                                                  ren2->dst_entry,
-                                                  o,
-                                                  ren1->src_entry,
-                                                  ren2->src_entry);
-
+                                                  opt, ren1, ren2);
                } else {
                        /* Renamed in 1, maybe changed in 2 */
                        /* we only use sha1 and mode of these */
@@ -2788,8 +2737,8 @@ static int process_renames(struct merge_options *o,
                         * stage and in other_stage (think of rename +
                         * add-source case).
                         */
-                       remove_file(o, 1, ren1_src,
-                                   renamed_stage == 2 || !was_tracked(o, ren1_src));
+                       remove_file(opt, 1, ren1_src,
+                                   renamed_stage == 2 || !was_tracked(opt, ren1_src));
 
                        oidcpy(&src_other.oid,
                               &ren1->src_entry->stages[other_stage].oid);
@@ -2800,28 +2749,12 @@ static int process_renames(struct merge_options *o,
                        try_merge = 0;
 
                        if (oid_eq(&src_other.oid, &null_oid) &&
-                           ren1->add_turned_into_rename) {
+                           ren1->dir_rename_original_type == 'A') {
                                setup_rename_conflict_info(RENAME_VIA_DIR,
-                                                          ren1->pair,
-                                                          NULL,
-                                                          branch1,
-                                                          branch2,
-                                                          ren1->dst_entry,
-                                                          NULL,
-                                                          o,
-                                                          NULL,
-                                                          NULL);
+                                                          opt, ren1, NULL);
                        } else if (oid_eq(&src_other.oid, &null_oid)) {
                                setup_rename_conflict_info(RENAME_DELETE,
-                                                          ren1->pair,
-                                                          NULL,
-                                                          branch1,
-                                                          branch2,
-                                                          ren1->dst_entry,
-                                                          NULL,
-                                                          o,
-                                                          NULL,
-                                                          NULL);
+                                                          opt, ren1, NULL);
                        } else if ((dst_other.mode == ren1->pair->two->mode) &&
                                   oid_eq(&dst_other.oid, &ren1->pair->two->oid)) {
                                /*
@@ -2832,9 +2765,8 @@ static int process_renames(struct merge_options *o,
                                 * update_file_flags() instead of
                                 * update_file().
                                 */
-                               if (update_file_flags(o,
-                                                     &ren1->pair->two->oid,
-                                                     ren1->pair->two->mode,
+                               if (update_file_flags(opt,
+                                                     ren1->pair->two,
                                                      ren1_dst,
                                                      1, /* update_cache */
                                                      0  /* update_wd    */))
@@ -2848,25 +2780,17 @@ static int process_renames(struct merge_options *o,
                                 * file, then the merge will be clean.
                                 */
                                setup_rename_conflict_info(RENAME_ADD,
-                                                          ren1->pair,
-                                                          NULL,
-                                                          branch1,
-                                                          branch2,
-                                                          ren1->dst_entry,
-                                                          NULL,
-                                                          o,
-                                                          ren1->src_entry,
-                                                          NULL);
+                                                          opt, ren1, NULL);
                        } else
                                try_merge = 1;
 
                        if (clean_merge < 0)
                                goto cleanup_and_return;
                        if (try_merge) {
-                               struct diff_filespec *one, *a, *b;
+                               struct diff_filespec *o, *a, *b;
                                src_other.path = (char *)ren1_src;
 
-                               one = ren1->pair->one;
+                               o = ren1->pair->one;
                                if (a_renames == renames1) {
                                        a = ren1->pair->two;
                                        b = &src_other;
@@ -2874,17 +2798,9 @@ static int process_renames(struct merge_options *o,
                                        b = ren1->pair->two;
                                        a = &src_other;
                                }
-                               update_entry(ren1->dst_entry, one, a, b);
+                               update_entry(ren1->dst_entry, o, a, b);
                                setup_rename_conflict_info(RENAME_NORMAL,
-                                                          ren1->pair,
-                                                          NULL,
-                                                          branch1,
-                                                          NULL,
-                                                          ren1->dst_entry,
-                                                          NULL,
-                                                          o,
-                                                          NULL,
-                                                          NULL);
+                                                          opt, ren1, NULL);
                        }
                }
        }
@@ -2919,7 +2835,7 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,
        free(pairs);
 }
 
-static int detect_and_process_renames(struct merge_options *o,
+static int detect_and_process_renames(struct merge_options *opt,
                                      struct tree *common,
                                      struct tree *head,
                                      struct tree *merge,
@@ -2933,17 +2849,17 @@ static int detect_and_process_renames(struct merge_options *o,
        ri->head_renames = NULL;
        ri->merge_renames = NULL;
 
-       if (!merge_detect_rename(o))
+       if (!merge_detect_rename(opt))
                return 1;
 
-       head_pairs = get_diffpairs(o, common, head);
-       merge_pairs = get_diffpairs(o, common, merge);
+       head_pairs = get_diffpairs(opt, common, head);
+       merge_pairs = get_diffpairs(opt, common, merge);
 
-       if (o->detect_directory_renames) {
+       if (opt->detect_directory_renames) {
                dir_re_head = get_directory_renames(head_pairs);
                dir_re_merge = get_directory_renames(merge_pairs);
 
-               handle_directory_level_conflicts(o,
+               handle_directory_level_conflicts(opt,
                                                 dir_re_head, head,
                                                 dir_re_merge, merge);
        } else {
@@ -2953,19 +2869,19 @@ static int detect_and_process_renames(struct merge_options *o,
                dir_rename_init(dir_re_merge);
        }
 
-       ri->head_renames  = get_renames(o, head_pairs,
+       ri->head_renames  = get_renames(opt, opt->branch1, head_pairs,
                                        dir_re_merge, dir_re_head, head,
                                        common, head, merge, entries,
                                        &clean);
        if (clean < 0)
                goto cleanup;
-       ri->merge_renames = get_renames(o, merge_pairs,
+       ri->merge_renames = get_renames(opt, opt->branch2, merge_pairs,
                                        dir_re_head, dir_re_merge, merge,
                                        common, head, merge, entries,
                                        &clean);
        if (clean < 0)
                goto cleanup;
-       clean &= process_renames(o, ri->head_renames, ri->merge_renames);
+       clean &= process_renames(opt, ri->head_renames, ri->merge_renames);
 
 cleanup:
        /*
@@ -3001,12 +2917,7 @@ static void final_cleanup_renames(struct rename_info *re_info)
        final_cleanup_rename(re_info->merge_renames);
 }
 
-static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)
-{
-       return (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;
-}
-
-static int read_oid_strbuf(struct merge_options *o,
+static int read_oid_strbuf(struct merge_options *opt,
                           const struct object_id *oid,
                           struct strbuf *dst)
 {
@@ -3015,130 +2926,99 @@ static int read_oid_strbuf(struct merge_options *o,
        unsigned long size;
        buf = read_object_file(oid, &type, &size);
        if (!buf)
-               return err(o, _("cannot read object %s"), oid_to_hex(oid));
+               return err(opt, _("cannot read object %s"), oid_to_hex(oid));
        if (type != OBJ_BLOB) {
                free(buf);
-               return err(o, _("object %s is not a blob"), oid_to_hex(oid));
+               return err(opt, _("object %s is not a blob"), oid_to_hex(oid));
        }
        strbuf_attach(dst, buf, size, size + 1);
        return 0;
 }
 
 static int blob_unchanged(struct merge_options *opt,
-                         const struct object_id *o_oid,
-                         unsigned o_mode,
-                         const struct object_id *a_oid,
-                         unsigned a_mode,
+                         const struct diff_filespec *o,
+                         const struct diff_filespec *a,
                          int renormalize, const char *path)
 {
-       struct strbuf o = STRBUF_INIT;
-       struct strbuf a = STRBUF_INIT;
+       struct strbuf obuf = STRBUF_INIT;
+       struct strbuf abuf = STRBUF_INIT;
        int ret = 0; /* assume changed for safety */
+       const struct index_state *idx = opt->repo->index;
 
-       if (a_mode != o_mode)
+       if (a->mode != o->mode)
                return 0;
-       if (oid_eq(o_oid, a_oid))
+       if (oid_eq(&o->oid, &a->oid))
                return 1;
        if (!renormalize)
                return 0;
 
-       assert(o_oid && a_oid);
-       if (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a))
+       if (read_oid_strbuf(opt, &o->oid, &obuf) ||
+           read_oid_strbuf(opt, &a->oid, &abuf))
                goto error_return;
        /*
         * Note: binary | is used so that both renormalizations are
         * performed.  Comparison can be skipped if both files are
         * unchanged since their sha1s have already been compared.
         */
-       if (renormalize_buffer(opt->repo->index, path, o.buf, o.len, &o) |
-           renormalize_buffer(opt->repo->index, path, a.buf, a.len, &a))
-               ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
+       if (renormalize_buffer(idx, path, obuf.buf, obuf.len, &obuf) |
+           renormalize_buffer(idx, path, abuf.buf, abuf.len, &abuf))
+               ret = (obuf.len == abuf.len && !memcmp(obuf.buf, abuf.buf, obuf.len));
 
 error_return:
-       strbuf_release(&o);
-       strbuf_release(&a);
+       strbuf_release(&obuf);
+       strbuf_release(&abuf);
        return ret;
 }
 
-static int handle_modify_delete(struct merge_options *o,
+static int handle_modify_delete(struct merge_options *opt,
                                const char *path,
-                               struct object_id *o_oid, int o_mode,
-                               struct object_id *a_oid, int a_mode,
-                               struct object_id *b_oid, int b_mode)
+                               const struct diff_filespec *o,
+                               const struct diff_filespec *a,
+                               const struct diff_filespec *b)
 {
        const char *modify_branch, *delete_branch;
-       struct object_id *changed_oid;
-       int changed_mode;
-
-       if (a_oid) {
-               modify_branch = o->branch1;
-               delete_branch = o->branch2;
-               changed_oid = a_oid;
-               changed_mode = a_mode;
+       const struct diff_filespec *changed;
+
+       if (is_valid(a)) {
+               modify_branch = opt->branch1;
+               delete_branch = opt->branch2;
+               changed = a;
        } else {
-               modify_branch = o->branch2;
-               delete_branch = o->branch1;
-               changed_oid = b_oid;
-               changed_mode = b_mode;
+               modify_branch = opt->branch2;
+               delete_branch = opt->branch1;
+               changed = b;
        }
 
-       return handle_change_delete(o,
+       return handle_change_delete(opt,
                                    path, NULL,
-                                   o_oid, o_mode,
-                                   changed_oid, changed_mode,
+                                   o, changed,
                                    modify_branch, delete_branch,
                                    _("modify"), _("modified"));
 }
 
-static int handle_content_merge(struct merge_options *o,
+static int handle_content_merge(struct merge_file_info *mfi,
+                               struct merge_options *opt,
                                const char *path,
                                int is_dirty,
-                               struct object_id *o_oid, int o_mode,
-                               struct object_id *a_oid, int a_mode,
-                               struct object_id *b_oid, int b_mode,
-                               struct rename_conflict_info *rename_conflict_info)
+                               const struct diff_filespec *o,
+                               const struct diff_filespec *a,
+                               const struct diff_filespec *b,
+                               struct rename_conflict_info *ci)
 {
        const char *reason = _("content");
-       const char *path1 = NULL, *path2 = NULL;
-       struct merge_file_info mfi;
-       struct diff_filespec one, a, b;
        unsigned df_conflict_remains = 0;
 
-       if (!o_oid) {
+       if (!is_valid(o))
                reason = _("add/add");
-               o_oid = (struct object_id *)&null_oid;
-       }
-       one.path = a.path = b.path = (char *)path;
-       oidcpy(&one.oid, o_oid);
-       one.mode = o_mode;
-       oidcpy(&a.oid, a_oid);
-       a.mode = a_mode;
-       oidcpy(&b.oid, b_oid);
-       b.mode = b_mode;
-
-       if (rename_conflict_info) {
-               struct diff_filepair *pair1 = rename_conflict_info->pair1;
-
-               path1 = (o->branch1 == rename_conflict_info->branch1) ?
-                       pair1->two->path : pair1->one->path;
-               /* If rename_conflict_info->pair2 != NULL, we are in
-                * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a
-                * normal rename.
-                */
-               path2 = (rename_conflict_info->pair2 ||
-                        o->branch2 == rename_conflict_info->branch1) ?
-                       pair1->two->path : pair1->one->path;
-               one.path = pair1->one->path;
-               a.path = (char *)path1;
-               b.path = (char *)path2;
-
-               if (dir_in_way(o->repo->index, path, !o->call_depth,
-                              S_ISGITLINK(pair1->two->mode)))
-                       df_conflict_remains = 1;
-       }
-       if (merge_mode_and_contents(o, &one, &a, &b, path,
-                                   o->branch1, o->branch2,
-                                   o->call_depth * 2, &mfi))
+
+       assert(o->path && a->path && b->path);
+       if (ci && dir_in_way(opt->repo->index, path, !opt->call_depth,
+                            S_ISGITLINK(ci->ren1->pair->two->mode)))
+               df_conflict_remains = 1;
+
+       if (merge_mode_and_contents(opt, o, a, b, path,
+                                   opt->branch1, opt->branch2,
+                                   opt->call_depth * 2, mfi))
                return -1;
 
        /*
@@ -3147,15 +3027,14 @@ static int handle_content_merge(struct merge_options *o,
         *   b) The merge matches what was in HEAD (content, mode, pathname)
         *   c) The target path is usable (i.e. not involved in D/F conflict)
         */
-       if (mfi.clean &&
-           was_tracked_and_matches(o, path, &mfi.oid, mfi.mode) &&
+       if (mfi->clean && was_tracked_and_matches(opt, path, &mfi->blob) &&
            !df_conflict_remains) {
                int pos;
                struct cache_entry *ce;
 
-               output(o, 3, _("Skipped %s (merged same as existing)"), path);
-               if (add_cacheinfo(o, mfi.mode, &mfi.oid, path,
-                                 0, (!o->call_depth && !is_dirty), 0))
+               output(opt, 3, _("Skipped %s (merged same as existing)"), path);
+               if (add_cacheinfo(opt, &mfi->blob, path,
+                                 0, (!opt->call_depth && !is_dirty), 0))
                        return -1;
                /*
                 * However, add_cacheinfo() will delete the old cache entry
@@ -3163,109 +3042,196 @@ static int handle_content_merge(struct merge_options *o,
                 * flag to avoid making the file appear as if it were
                 * deleted by the user.
                 */
-               pos = index_name_pos(&o->orig_index, path, strlen(path));
-               ce = o->orig_index.cache[pos];
+               pos = index_name_pos(&opt->orig_index, path, strlen(path));
+               ce = opt->orig_index.cache[pos];
                if (ce_skip_worktree(ce)) {
-                       pos = index_name_pos(o->repo->index, path, strlen(path));
-                       ce = o->repo->index->cache[pos];
+                       pos = index_name_pos(opt->repo->index, path, strlen(path));
+                       ce = opt->repo->index->cache[pos];
                        ce->ce_flags |= CE_SKIP_WORKTREE;
                }
-               return mfi.clean;
+               return mfi->clean;
        }
 
-       if (!mfi.clean) {
-               if (S_ISGITLINK(mfi.mode))
+       if (!mfi->clean) {
+               if (S_ISGITLINK(mfi->blob.mode))
                        reason = _("submodule");
-               output(o, 1, _("CONFLICT (%s): Merge conflict in %s"),
+               output(opt, 1, _("CONFLICT (%s): Merge conflict in %s"),
                                reason, path);
-               if (rename_conflict_info && !df_conflict_remains)
-                       if (update_stages(o, path, &one, &a, &b))
+               if (ci && !df_conflict_remains)
+                       if (update_stages(opt, path, o, a, b))
                                return -1;
        }
 
        if (df_conflict_remains || is_dirty) {
                char *new_path;
-               if (o->call_depth) {
-                       remove_file_from_index(o->repo->index, path);
+               if (opt->call_depth) {
+                       remove_file_from_index(opt->repo->index, path);
                } else {
-                       if (!mfi.clean) {
-                               if (update_stages(o, path, &one, &a, &b))
+                       if (!mfi->clean) {
+                               if (update_stages(opt, path, o, a, b))
                                        return -1;
                        } else {
-                               int file_from_stage2 = was_tracked(o, path);
-                               struct diff_filespec merged;
-                               oidcpy(&merged.oid, &mfi.oid);
-                               merged.mode = mfi.mode;
-
-                               if (update_stages(o, path, NULL,
-                                                 file_from_stage2 ? &merged : NULL,
-                                                 file_from_stage2 ? NULL : &merged))
+                               int file_from_stage2 = was_tracked(opt, path);
+
+                               if (update_stages(opt, path, NULL,
+                                                 file_from_stage2 ? &mfi->blob : NULL,
+                                                 file_from_stage2 ? NULL : &mfi->blob))
                                        return -1;
                        }
 
                }
-               new_path = unique_path(o, path, rename_conflict_info->branch1);
+               new_path = unique_path(opt, path, ci->ren1->branch);
                if (is_dirty) {
-                       output(o, 1, _("Refusing to lose dirty file at %s"),
+                       output(opt, 1, _("Refusing to lose dirty file at %s"),
                               path);
                }
-               output(o, 1, _("Adding as %s instead"), new_path);
-               if (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) {
+               output(opt, 1, _("Adding as %s instead"), new_path);
+               if (update_file(opt, 0, &mfi->blob, new_path)) {
                        free(new_path);
                        return -1;
                }
                free(new_path);
-               mfi.clean = 0;
-       } else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path))
+               mfi->clean = 0;
+       } else if (update_file(opt, mfi->clean, &mfi->blob, path))
                return -1;
-       return !is_dirty && mfi.clean;
+       return !is_dirty && mfi->clean;
 }
 
-static int handle_rename_normal(struct merge_options *o,
+static int handle_rename_normal(struct merge_options *opt,
                                const char *path,
-                               struct object_id *o_oid, unsigned int o_mode,
-                               struct object_id *a_oid, unsigned int a_mode,
-                               struct object_id *b_oid, unsigned int b_mode,
+                               const struct diff_filespec *o,
+                               const struct diff_filespec *a,
+                               const struct diff_filespec *b,
                                struct rename_conflict_info *ci)
 {
+       struct rename *ren = ci->ren1;
+       struct merge_file_info mfi;
+       int clean;
+       int side = (ren->branch == opt->branch1 ? 2 : 3);
+
        /* Merge the content and write it out */
-       return handle_content_merge(o, path, was_dirty(o, path),
-                                   o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
-                                   ci);
+       clean = handle_content_merge(&mfi, opt, path, was_dirty(opt, path),
+                                    o, a, b, ci);
+
+       if (clean && opt->detect_directory_renames == 1 &&
+           ren->dir_rename_original_dest) {
+               if (update_stages(opt, path,
+                                 NULL,
+                                 side == 2 ? &mfi.blob : NULL,
+                                 side == 2 ? NULL : &mfi.blob))
+                       return -1;
+               clean = 0; /* not clean, but conflicted */
+       }
+       return clean;
+}
+
+static void dir_rename_warning(const char *msg,
+                              int is_add,
+                              int clean,
+                              struct merge_options *opt,
+                              struct rename *ren)
+{
+       const char *other_branch;
+       other_branch = (ren->branch == opt->branch1 ?
+                       opt->branch2 : opt->branch1);
+       if (is_add) {
+               output(opt, clean ? 2 : 1, msg,
+                      ren->pair->one->path, ren->branch,
+                      other_branch, ren->pair->two->path);
+               return;
+       }
+       output(opt, clean ? 2 : 1, msg,
+              ren->pair->one->path, ren->dir_rename_original_dest, ren->branch,
+              other_branch, ren->pair->two->path);
+}
+static int warn_about_dir_renamed_entries(struct merge_options *opt,
+                                         struct rename *ren)
+{
+       const char *msg;
+       int clean = 1, is_add;
+
+       if (!ren)
+               return clean;
+
+       /* Return early if ren was not affected/created by a directory rename */
+       if (!ren->dir_rename_original_dest)
+               return clean;
+
+       /* Sanity checks */
+       assert(opt->detect_directory_renames > 0);
+       assert(ren->dir_rename_original_type == 'A' ||
+              ren->dir_rename_original_type == 'R');
+
+       /* Check whether to treat directory renames as a conflict */
+       clean = (opt->detect_directory_renames == 2);
+
+       is_add = (ren->dir_rename_original_type == 'A');
+       if (ren->dir_rename_original_type == 'A' && clean) {
+               msg = _("Path updated: %s added in %s inside a "
+                       "directory that was renamed in %s; moving it to %s.");
+       } else if (ren->dir_rename_original_type == 'A' && !clean) {
+               msg = _("CONFLICT (file location): %s added in %s "
+                       "inside a directory that was renamed in %s, "
+                       "suggesting it should perhaps be moved to %s.");
+       } else if (ren->dir_rename_original_type == 'R' && clean) {
+               msg = _("Path updated: %s renamed to %s in %s, inside a "
+                       "directory that was renamed in %s; moving it to %s.");
+       } else if (ren->dir_rename_original_type == 'R' && !clean) {
+               msg = _("CONFLICT (file location): %s renamed to %s in %s, "
+                       "inside a directory that was renamed in %s, "
+                       "suggesting it should perhaps be moved to %s.");
+       } else {
+               BUG("Impossible dir_rename_original_type/clean combination");
+       }
+       dir_rename_warning(msg, is_add, clean, opt, ren);
+
+       return clean;
 }
 
 /* Per entry merge function */
-static int process_entry(struct merge_options *o,
+static int process_entry(struct merge_options *opt,
                         const char *path, struct stage_data *entry)
 {
        int clean_merge = 1;
-       int normalize = o->renormalize;
-       unsigned o_mode = entry->stages[1].mode;
-       unsigned a_mode = entry->stages[2].mode;
-       unsigned b_mode = entry->stages[3].mode;
-       struct object_id *o_oid = stage_oid(&entry->stages[1].oid, o_mode);
-       struct object_id *a_oid = stage_oid(&entry->stages[2].oid, a_mode);
-       struct object_id *b_oid = stage_oid(&entry->stages[3].oid, b_mode);
+       int normalize = opt->renormalize;
+
+       struct diff_filespec *o = &entry->stages[1];
+       struct diff_filespec *a = &entry->stages[2];
+       struct diff_filespec *b = &entry->stages[3];
+       int o_valid = is_valid(o);
+       int a_valid = is_valid(a);
+       int b_valid = is_valid(b);
+       o->path = a->path = b->path = (char*)path;
 
        entry->processed = 1;
        if (entry->rename_conflict_info) {
-               struct rename_conflict_info *conflict_info = entry->rename_conflict_info;
-               switch (conflict_info->rename_type) {
+               struct rename_conflict_info *ci = entry->rename_conflict_info;
+               struct diff_filespec *temp;
+               int path_clean;
+
+               path_clean = warn_about_dir_renamed_entries(opt, ci->ren1);
+               path_clean &= warn_about_dir_renamed_entries(opt, ci->ren2);
+
+               /*
+                * For cases with a single rename, {o,a,b}->path have all been
+                * set to the rename target path; we need to set two of these
+                * back to the rename source.
+                * For rename/rename conflicts, we'll manually fix paths below.
+                */
+               temp = (opt->branch1 == ci->ren1->branch) ? b : a;
+               o->path = temp->path = ci->ren1->pair->one->path;
+               if (ci->ren2) {
+                       assert(opt->branch1 == ci->ren1->branch);
+               }
+
+               switch (ci->rename_type) {
                case RENAME_NORMAL:
                case RENAME_ONE_FILE_TO_ONE:
-                       clean_merge = handle_rename_normal(o,
-                                                          path,
-                                                          o_oid, o_mode,
-                                                          a_oid, a_mode,
-                                                          b_oid, b_mode,
-                                                          conflict_info);
+                       clean_merge = handle_rename_normal(opt, path, o, a, b,
+                                                          ci);
                        break;
                case RENAME_VIA_DIR:
-                       clean_merge = 1;
-                       if (handle_rename_via_dir(o,
-                                                 conflict_info->pair1,
-                                                 conflict_info->branch1))
-                               clean_merge = -1;
+                       clean_merge = handle_rename_via_dir(opt, ci);
                        break;
                case RENAME_ADD:
                        /*
@@ -3274,165 +3240,172 @@ static int process_entry(struct merge_options *o,
                         * two-way merged cleanly with the added file, I
                         * guess it's a clean merge?
                         */
-                       clean_merge = handle_rename_add(o, conflict_info);
+                       clean_merge = handle_rename_add(opt, ci);
                        break;
                case RENAME_DELETE:
                        clean_merge = 0;
-                       if (handle_rename_delete(o,
-                                                conflict_info->pair1,
-                                                conflict_info->branch1,
-                                                conflict_info->branch2))
+                       if (handle_rename_delete(opt, ci))
                                clean_merge = -1;
                        break;
                case RENAME_ONE_FILE_TO_TWO:
+                       /*
+                        * Manually fix up paths; note:
+                        * ren[12]->pair->one->path are equal.
+                        */
+                       o->path = ci->ren1->pair->one->path;
+                       a->path = ci->ren1->pair->two->path;
+                       b->path = ci->ren2->pair->two->path;
+
                        clean_merge = 0;
-                       if (handle_rename_rename_1to2(o, conflict_info))
+                       if (handle_rename_rename_1to2(opt, ci))
                                clean_merge = -1;
                        break;
                case RENAME_TWO_FILES_TO_ONE:
+                       /*
+                        * Manually fix up paths; note,
+                        * ren[12]->pair->two->path are actually equal.
+                        */
+                       o->path = NULL;
+                       a->path = ci->ren1->pair->two->path;
+                       b->path = ci->ren2->pair->two->path;
+
                        /*
                         * Probably unclean merge, but if the two renamed
                         * files merge cleanly and the two resulting files
                         * can then be two-way merged cleanly, I guess it's
                         * a clean merge?
                         */
-                       clean_merge = handle_rename_rename_2to1(o,
-                                                               conflict_info);
+                       clean_merge = handle_rename_rename_2to1(opt, ci);
                        break;
                default:
                        entry->processed = 0;
                        break;
                }
-       } else if (o_oid && (!a_oid || !b_oid)) {
+               if (path_clean < clean_merge)
+                       clean_merge = path_clean;
+       } else if (o_valid && (!a_valid || !b_valid)) {
                /* Case A: Deleted in one */
-               if ((!a_oid && !b_oid) ||
-                   (!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||
-                   (!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) {
+               if ((!a_valid && !b_valid) ||
+                   (!b_valid && blob_unchanged(opt, o, a, normalize, path)) ||
+                   (!a_valid && blob_unchanged(opt, o, b, normalize, path))) {
                        /* Deleted in both or deleted in one and
                         * unchanged in the other */
-                       if (a_oid)
-                               output(o, 2, _("Removing %s"), path);
+                       if (a_valid)
+                               output(opt, 2, _("Removing %s"), path);
                        /* do not touch working file if it did not exist */
-                       remove_file(o, 1, path, !a_oid);
+                       remove_file(opt, 1, path, !a_valid);
                } else {
                        /* Modify/delete; deleted side may have put a directory in the way */
                        clean_merge = 0;
-                       if (handle_modify_delete(o, path, o_oid, o_mode,
-                                                a_oid, a_mode, b_oid, b_mode))
+                       if (handle_modify_delete(opt, path, o, a, b))
                                clean_merge = -1;
                }
-       } else if ((!o_oid && a_oid && !b_oid) ||
-                  (!o_oid && !a_oid && b_oid)) {
+       } else if ((!o_valid && a_valid && !b_valid) ||
+                  (!o_valid && !a_valid && b_valid)) {
                /* Case B: Added in one. */
                /* [nothing|directory] -> ([nothing|directory], file) */
 
                const char *add_branch;
                const char *other_branch;
-               unsigned mode;
-               const struct object_id *oid;
                const char *conf;
+               const struct diff_filespec *contents;
 
-               if (a_oid) {
-                       add_branch = o->branch1;
-                       other_branch = o->branch2;
-                       mode = a_mode;
-                       oid = a_oid;
+               if (a_valid) {
+                       add_branch = opt->branch1;
+                       other_branch = opt->branch2;
+                       contents = a;
                        conf = _("file/directory");
                } else {
-                       add_branch = o->branch2;
-                       other_branch = o->branch1;
-                       mode = b_mode;
-                       oid = b_oid;
+                       add_branch = opt->branch2;
+                       other_branch = opt->branch1;
+                       contents = b;
                        conf = _("directory/file");
                }
-               if (dir_in_way(o->repo->index, path,
-                              !o->call_depth && !S_ISGITLINK(a_mode),
+               if (dir_in_way(opt->repo->index, path,
+                              !opt->call_depth && !S_ISGITLINK(a->mode),
                               0)) {
-                       char *new_path = unique_path(o, path, add_branch);
+                       char *new_path = unique_path(opt, path, add_branch);
                        clean_merge = 0;
-                       output(o, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
+                       output(opt, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
                               "Adding %s as %s"),
                               conf, path, other_branch, path, new_path);
-                       if (update_file(o, 0, oid, mode, new_path))
+                       if (update_file(opt, 0, contents, new_path))
                                clean_merge = -1;
-                       else if (o->call_depth)
-                               remove_file_from_index(o->repo->index, path);
+                       else if (opt->call_depth)
+                               remove_file_from_index(opt->repo->index, path);
                        free(new_path);
                } else {
-                       output(o, 2, _("Adding %s"), path);
+                       output(opt, 2, _("Adding %s"), path);
                        /* do not overwrite file if already present */
-                       if (update_file_flags(o, oid, mode, path, 1, !a_oid))
+                       if (update_file_flags(opt, contents, path, 1, !a_valid))
                                clean_merge = -1;
                }
-       } else if (a_oid && b_oid) {
-               if (!o_oid) {
+       } else if (a_valid && b_valid) {
+               if (!o_valid) {
                        /* Case C: Added in both (check for same permissions) */
-                       output(o, 1,
+                       output(opt, 1,
                               _("CONFLICT (add/add): Merge conflict in %s"),
                               path);
-                       clean_merge = handle_file_collision(o,
+                       clean_merge = handle_file_collision(opt,
                                                            path, NULL, NULL,
-                                                           o->branch1,
-                                                           o->branch2,
-                                                           a_oid, a_mode,
-                                                           b_oid, b_mode);
+                                                           opt->branch1,
+                                                           opt->branch2,
+                                                           a, b);
                } else {
                        /* case D: Modified in both, but differently. */
+                       struct merge_file_info mfi;
                        int is_dirty = 0; /* unpack_trees would have bailed if dirty */
-                       clean_merge = handle_content_merge(o, path,
+                       clean_merge = handle_content_merge(&mfi, opt, path,
                                                           is_dirty,
-                                                          o_oid, o_mode,
-                                                          a_oid, a_mode,
-                                                          b_oid, b_mode,
-                                                          NULL);
+                                                          o, a, b, NULL);
                }
-       } else if (!o_oid && !a_oid && !b_oid) {
+       } else if (!o_valid && !a_valid && !b_valid) {
                /*
                 * this entry was deleted altogether. a_mode == 0 means
                 * we had that path and want to actively remove it.
                 */
-               remove_file(o, 1, path, !a_mode);
+               remove_file(opt, 1, path, !a->mode);
        } else
                BUG("fatal merge failure, shouldn't happen.");
 
        return clean_merge;
 }
 
-int merge_trees(struct merge_options *o,
+int merge_trees(struct merge_options *opt,
                struct tree *head,
                struct tree *merge,
                struct tree *common,
                struct tree **result)
 {
-       struct index_state *istate = o->repo->index;
+       struct index_state *istate = opt->repo->index;
        int code, clean;
        struct strbuf sb = STRBUF_INIT;
 
-       if (!o->call_depth && repo_index_has_changes(o->repo, head, &sb)) {
-               err(o, _("Your local changes to the following files would be overwritten by merge:\n  %s"),
+       if (!opt->call_depth && repo_index_has_changes(opt->repo, head, &sb)) {
+               err(opt, _("Your local changes to the following files would be overwritten by merge:\n  %s"),
                    sb.buf);
                return -1;
        }
 
-       if (o->subtree_shift) {
-               merge = shift_tree_object(o->repo, head, merge, o->subtree_shift);
-               common = shift_tree_object(o->repo, head, common, o->subtree_shift);
+       if (opt->subtree_shift) {
+               merge = shift_tree_object(opt->repo, head, merge, opt->subtree_shift);
+               common = shift_tree_object(opt->repo, head, common, opt->subtree_shift);
        }
 
        if (oid_eq(&common->object.oid, &merge->object.oid)) {
-               output(o, 0, _("Already up to date!"));
+               output(opt, 0, _("Already up to date!"));
                *result = head;
                return 1;
        }
 
-       code = unpack_trees_start(o, common, head, merge);
+       code = unpack_trees_start(opt, common, head, merge);
 
        if (code != 0) {
-               if (show(o, 4) || o->call_depth)
-                       err(o, _("merging of trees %s and %s failed"),
+               if (show(opt, 4) || opt->call_depth)
+                       err(opt, _("merging of trees %s and %s failed"),
                            oid_to_hex(&head->object.oid),
                            oid_to_hex(&merge->object.oid));
-               unpack_trees_finish(o);
+               unpack_trees_finish(opt);
                return -1;
        }
 
@@ -3447,21 +3420,21 @@ int merge_trees(struct merge_options *o,
                 * opposed to decaring a local hashmap is for convenience
                 * so that we don't have to pass it to around.
                 */
-               hashmap_init(&o->current_file_dir_set, path_hashmap_cmp, NULL, 512);
-               get_files_dirs(o, head);
-               get_files_dirs(o, merge);
+               hashmap_init(&opt->current_file_dir_set, path_hashmap_cmp, NULL, 512);
+               get_files_dirs(opt, head);
+               get_files_dirs(opt, merge);
 
-               entries = get_unmerged(o->repo->index);
-               clean = detect_and_process_renames(o, common, head, merge,
+               entries = get_unmerged(opt->repo->index);
+               clean = detect_and_process_renames(opt, common, head, merge,
                                                   entries, &re_info);
-               record_df_conflict_files(o, entries);
+               record_df_conflict_files(opt, entries);
                if (clean < 0)
                        goto cleanup;
                for (i = entries->nr-1; 0 <= i; i--) {
                        const char *path = entries->items[i].string;
                        struct stage_data *e = entries->items[i].util;
                        if (!e->processed) {
-                               int ret = process_entry(o, path, e);
+                               int ret = process_entry(opt, path, e);
                                if (!ret)
                                        clean = 0;
                                else if (ret < 0) {
@@ -3483,19 +3456,19 @@ int merge_trees(struct merge_options *o,
                string_list_clear(entries, 1);
                free(entries);
 
-               hashmap_free(&o->current_file_dir_set, 1);
+               hashmap_free(&opt->current_file_dir_set, 1);
 
                if (clean < 0) {
-                       unpack_trees_finish(o);
+                       unpack_trees_finish(opt);
                        return clean;
                }
        }
        else
                clean = 1;
 
-       unpack_trees_finish(o);
+       unpack_trees_finish(opt);
 
-       if (o->call_depth && !(*result = write_tree_from_memory(o)))
+       if (opt->call_depth && !(*result = write_tree_from_memory(opt)))
                return -1;
 
        return clean;
@@ -3516,7 +3489,7 @@ static struct commit_list *reverse_commit_list(struct commit_list *list)
  * Merge the commits h1 and h2, return the resulting virtual
  * commit object and a flag indicating the cleanness of the merge.
  */
-int merge_recursive(struct merge_options *o,
+int merge_recursive(struct merge_options *opt,
                    struct commit *h1,
                    struct commit *h2,
                    struct commit_list *ca,
@@ -3527,10 +3500,10 @@ int merge_recursive(struct merge_options *o,
        struct tree *mrtree;
        int clean;
 
-       if (show(o, 4)) {
-               output(o, 4, _("Merging:"));
-               output_commit_title(o, h1);
-               output_commit_title(o, h2);
+       if (show(opt, 4)) {
+               output(opt, 4, _("Merging:"));
+               output_commit_title(opt, h1);
+               output_commit_title(opt, h2);
        }
 
        if (!ca) {
@@ -3538,13 +3511,13 @@ int merge_recursive(struct merge_options *o,
                ca = reverse_commit_list(ca);
        }
 
-       if (show(o, 5)) {
+       if (show(opt, 5)) {
                unsigned cnt = commit_list_count(ca);
 
-               output(o, 5, Q_("found %u common ancestor:",
+               output(opt, 5, Q_("found %u common ancestor:",
                                "found %u common ancestors:", cnt), cnt);
                for (iter = ca; iter; iter = iter->next)
-                       output_commit_title(o, iter->item);
+                       output_commit_title(opt, iter->item);
        }
 
        merged_common_ancestors = pop_commit(&ca);
@@ -3552,13 +3525,13 @@ int merge_recursive(struct merge_options *o,
                /* if there is no common ancestor, use an empty tree */
                struct tree *tree;
 
-               tree = lookup_tree(o->repo, o->repo->hash_algo->empty_tree);
-               merged_common_ancestors = make_virtual_commit(o->repo, tree, "ancestor");
+               tree = lookup_tree(opt->repo, opt->repo->hash_algo->empty_tree);
+               merged_common_ancestors = make_virtual_commit(opt->repo, tree, "ancestor");
        }
 
        for (iter = ca; iter; iter = iter->next) {
                const char *saved_b1, *saved_b2;
-               o->call_depth++;
+               opt->call_depth++;
                /*
                 * When the merge fails, the result contains files
                 * with conflict markers. The cleanness flag is
@@ -3567,46 +3540,46 @@ int merge_recursive(struct merge_options *o,
                 * overwritten it: the committed "conflicts" were
                 * already resolved.
                 */
-               discard_index(o->repo->index);
-               saved_b1 = o->branch1;
-               saved_b2 = o->branch2;
-               o->branch1 = "Temporary merge branch 1";
-               o->branch2 = "Temporary merge branch 2";
-               if (merge_recursive(o, merged_common_ancestors, iter->item,
+               discard_index(opt->repo->index);
+               saved_b1 = opt->branch1;
+               saved_b2 = opt->branch2;
+               opt->branch1 = "Temporary merge branch 1";
+               opt->branch2 = "Temporary merge branch 2";
+               if (merge_recursive(opt, merged_common_ancestors, iter->item,
                                    NULL, &merged_common_ancestors) < 0)
                        return -1;
-               o->branch1 = saved_b1;
-               o->branch2 = saved_b2;
-               o->call_depth--;
+               opt->branch1 = saved_b1;
+               opt->branch2 = saved_b2;
+               opt->call_depth--;
 
                if (!merged_common_ancestors)
-                       return err(o, _("merge returned no commit"));
+                       return err(opt, _("merge returned no commit"));
        }
 
-       discard_index(o->repo->index);
-       if (!o->call_depth)
-               repo_read_index(o->repo);
+       discard_index(opt->repo->index);
+       if (!opt->call_depth)
+               repo_read_index(opt->repo);
 
-       o->ancestor = "merged common ancestors";
-       clean = merge_trees(o, get_commit_tree(h1), get_commit_tree(h2),
+       opt->ancestor = "merged common ancestors";
+       clean = merge_trees(opt, get_commit_tree(h1), get_commit_tree(h2),
                            get_commit_tree(merged_common_ancestors),
                            &mrtree);
        if (clean < 0) {
-               flush_output(o);
+               flush_output(opt);
                return clean;
        }
 
-       if (o->call_depth) {
-               *result = make_virtual_commit(o->repo, mrtree, "merged tree");
+       if (opt->call_depth) {
+               *result = make_virtual_commit(opt->repo, mrtree, "merged tree");
                commit_list_insert(h1, &(*result)->parents);
                commit_list_insert(h2, &(*result)->parents->next);
        }
-       flush_output(o);
-       if (!o->call_depth && o->buffer_output < 2)
-               strbuf_release(&o->obuf);
-       if (show(o, 2))
+       flush_output(opt);
+       if (!opt->call_depth && opt->buffer_output < 2)
+               strbuf_release(&opt->obuf);
+       if (show(opt, 2))
                diff_warn_rename_limit("merge.renamelimit",
-                                      o->needed_rename_limit, 0);
+                                      opt->needed_rename_limit, 0);
        return clean;
 }
 
@@ -3628,7 +3601,7 @@ static struct commit *get_ref(struct repository *repo, const struct object_id *o
        return (struct commit *)object;
 }
 
-int merge_recursive_generic(struct merge_options *o,
+int merge_recursive_generic(struct merge_options *opt,
                            const struct object_id *head,
                            const struct object_id *merge,
                            int num_base_list,
@@ -3637,127 +3610,136 @@ int merge_recursive_generic(struct merge_options *o,
 {
        int clean;
        struct lock_file lock = LOCK_INIT;
-       struct commit *head_commit = get_ref(o->repo, head, o->branch1);
-       struct commit *next_commit = get_ref(o->repo, merge, o->branch2);
+       struct commit *head_commit = get_ref(opt->repo, head, opt->branch1);
+       struct commit *next_commit = get_ref(opt->repo, merge, opt->branch2);
        struct commit_list *ca = NULL;
 
        if (base_list) {
                int i;
                for (i = 0; i < num_base_list; ++i) {
                        struct commit *base;
-                       if (!(base = get_ref(o->repo, base_list[i], oid_to_hex(base_list[i]))))
-                               return err(o, _("Could not parse object '%s'"),
+                       if (!(base = get_ref(opt->repo, base_list[i], oid_to_hex(base_list[i]))))
+                               return err(opt, _("Could not parse object '%s'"),
                                           oid_to_hex(base_list[i]));
                        commit_list_insert(base, &ca);
                }
        }
 
-       repo_hold_locked_index(o->repo, &lock, LOCK_DIE_ON_ERROR);
-       clean = merge_recursive(o, head_commit, next_commit, ca,
+       repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);
+       clean = merge_recursive(opt, head_commit, next_commit, ca,
                                result);
        if (clean < 0) {
                rollback_lock_file(&lock);
                return clean;
        }
 
-       if (write_locked_index(o->repo->index, &lock,
+       if (write_locked_index(opt->repo->index, &lock,
                               COMMIT_LOCK | SKIP_IF_UNCHANGED))
-               return err(o, _("Unable to write index."));
+               return err(opt, _("Unable to write index."));
 
        return clean ? 0 : 1;
 }
 
-static void merge_recursive_config(struct merge_options *o)
+static void merge_recursive_config(struct merge_options *opt)
 {
        char *value = NULL;
-       git_config_get_int("merge.verbosity", &o->verbosity);
-       git_config_get_int("diff.renamelimit", &o->diff_rename_limit);
-       git_config_get_int("merge.renamelimit", &o->merge_rename_limit);
+       git_config_get_int("merge.verbosity", &opt->verbosity);
+       git_config_get_int("diff.renamelimit", &opt->diff_rename_limit);
+       git_config_get_int("merge.renamelimit", &opt->merge_rename_limit);
        if (!git_config_get_string("diff.renames", &value)) {
-               o->diff_detect_rename = git_config_rename("diff.renames", value);
+               opt->diff_detect_rename = git_config_rename("diff.renames", value);
                free(value);
        }
        if (!git_config_get_string("merge.renames", &value)) {
-               o->merge_detect_rename = git_config_rename("merge.renames", value);
+               opt->merge_detect_rename = git_config_rename("merge.renames", value);
+               free(value);
+       }
+       if (!git_config_get_string("merge.directoryrenames", &value)) {
+               int boolval = git_parse_maybe_bool(value);
+               if (0 <= boolval) {
+                       opt->detect_directory_renames = boolval ? 2 : 0;
+               } else if (!strcasecmp(value, "conflict")) {
+                       opt->detect_directory_renames = 1;
+               } /* avoid erroring on values from future versions of git */
                free(value);
        }
        git_config(git_xmerge_config, NULL);
 }
 
-void init_merge_options(struct merge_options *o,
+void init_merge_options(struct merge_options *opt,
                        struct repository *repo)
 {
        const char *merge_verbosity;
-       memset(o, 0, sizeof(struct merge_options));
-       o->repo = repo;
-       o->verbosity = 2;
-       o->buffer_output = 1;
-       o->diff_rename_limit = -1;
-       o->merge_rename_limit = -1;
-       o->renormalize = 0;
-       o->diff_detect_rename = -1;
-       o->merge_detect_rename = -1;
-       o->detect_directory_renames = 1;
-       merge_recursive_config(o);
+       memset(opt, 0, sizeof(struct merge_options));
+       opt->repo = repo;
+       opt->verbosity = 2;
+       opt->buffer_output = 1;
+       opt->diff_rename_limit = -1;
+       opt->merge_rename_limit = -1;
+       opt->renormalize = 0;
+       opt->diff_detect_rename = -1;
+       opt->merge_detect_rename = -1;
+       opt->detect_directory_renames = 1;
+       merge_recursive_config(opt);
        merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
        if (merge_verbosity)
-               o->verbosity = strtol(merge_verbosity, NULL, 10);
-       if (o->verbosity >= 5)
-               o->buffer_output = 0;
-       strbuf_init(&o->obuf, 0);
-       string_list_init(&o->df_conflict_file_set, 1);
+               opt->verbosity = strtol(merge_verbosity, NULL, 10);
+       if (opt->verbosity >= 5)
+               opt->buffer_output = 0;
+       strbuf_init(&opt->obuf, 0);
+       string_list_init(&opt->df_conflict_file_set, 1);
 }
 
-int parse_merge_opt(struct merge_options *o, const char *s)
+int parse_merge_opt(struct merge_options *opt, const char *s)
 {
        const char *arg;
 
        if (!s || !*s)
                return -1;
        if (!strcmp(s, "ours"))
-               o->recursive_variant = MERGE_RECURSIVE_OURS;
+               opt->recursive_variant = MERGE_RECURSIVE_OURS;
        else if (!strcmp(s, "theirs"))
-               o->recursive_variant = MERGE_RECURSIVE_THEIRS;
+               opt->recursive_variant = MERGE_RECURSIVE_THEIRS;
        else if (!strcmp(s, "subtree"))
-               o->subtree_shift = "";
+               opt->subtree_shift = "";
        else if (skip_prefix(s, "subtree=", &arg))
-               o->subtree_shift = arg;
+               opt->subtree_shift = arg;
        else if (!strcmp(s, "patience"))
-               o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
+               opt->xdl_opts = DIFF_WITH_ALG(opt, PATIENCE_DIFF);
        else if (!strcmp(s, "histogram"))
-               o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
+               opt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF);
        else if (skip_prefix(s, "diff-algorithm=", &arg)) {
                long value = parse_algorithm_value(arg);
                if (value < 0)
                        return -1;
                /* clear out previous settings */
-               DIFF_XDL_CLR(o, NEED_MINIMAL);
-               o->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
-               o->xdl_opts |= value;
+               DIFF_XDL_CLR(opt, NEED_MINIMAL);
+               opt->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
+               opt->xdl_opts |= value;
        }
        else if (!strcmp(s, "ignore-space-change"))
-               DIFF_XDL_SET(o, IGNORE_WHITESPACE_CHANGE);
+               DIFF_XDL_SET(opt, IGNORE_WHITESPACE_CHANGE);
        else if (!strcmp(s, "ignore-all-space"))
-               DIFF_XDL_SET(o, IGNORE_WHITESPACE);
+               DIFF_XDL_SET(opt, IGNORE_WHITESPACE);
        else if (!strcmp(s, "ignore-space-at-eol"))
-               DIFF_XDL_SET(o, IGNORE_WHITESPACE_AT_EOL);
+               DIFF_XDL_SET(opt, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(s, "ignore-cr-at-eol"))
-               DIFF_XDL_SET(o, IGNORE_CR_AT_EOL);
+               DIFF_XDL_SET(opt, IGNORE_CR_AT_EOL);
        else if (!strcmp(s, "renormalize"))
-               o->renormalize = 1;
+               opt->renormalize = 1;
        else if (!strcmp(s, "no-renormalize"))
-               o->renormalize = 0;
+               opt->renormalize = 0;
        else if (!strcmp(s, "no-renames"))
-               o->merge_detect_rename = 0;
+               opt->merge_detect_rename = 0;
        else if (!strcmp(s, "find-renames")) {
-               o->merge_detect_rename = 1;
-               o->rename_score = 0;
+               opt->merge_detect_rename = 1;
+               opt->rename_score = 0;
        }
        else if (skip_prefix(s, "find-renames=", &arg) ||
                 skip_prefix(s, "rename-threshold=", &arg)) {
-               if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
+               if ((opt->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
                        return -1;
-               o->merge_detect_rename = 1;
+               opt->merge_detect_rename = 1;
        }
        /*
         * Please update $__git_merge_strategy_options in
diff --git a/notes.c b/notes.c
index be72780a8f3bb5c28341b0125c06caa513ec1488..532ec37865768d05a31606f495b4f0c1645ea757 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -988,7 +988,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
                combine_notes_fn combine_notes, int flags)
 {
        struct object_id oid, object_oid;
-       unsigned mode;
+       unsigned short mode;
        struct leaf_node root_tree;
 
        if (!t)
index b086f5ecdb82a105b9b3381de9893cb72bc5871c..46a1da79cc8390de89114c127c706fb020d7c82c 100644 (file)
@@ -162,10 +162,10 @@ const char *loose_object_path(struct repository *r, struct strbuf *buf,
 void *map_loose_object(struct repository *r, const struct object_id *oid,
                       unsigned long *size);
 
-extern void *read_object_file_extended(struct repository *r,
-                                      const struct object_id *oid,
-                                      enum object_type *type,
-                                      unsigned long *size, int lookup_replace);
+void *read_object_file_extended(struct repository *r,
+                               const struct object_id *oid,
+                               enum object_type *type,
+                               unsigned long *size, int lookup_replace);
 static inline void *repo_read_object_file(struct repository *r,
                                          const struct object_id *oid,
                                          enum object_type *type,
@@ -180,20 +180,20 @@ static inline void *repo_read_object_file(struct repository *r,
 /* Read and unpack an object file into memory, write memory to an object file */
 int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
 
-extern int hash_object_file(const void *buf, unsigned long len,
-                           const char *type, struct object_id *oid);
+int hash_object_file(const void *buf, unsigned long len,
+                    const char *type, struct object_id *oid);
 
-extern int write_object_file(const void *buf, unsigned long len,
-                            const char *type, struct object_id *oid);
+int write_object_file(const void *buf, unsigned long len,
+                     const char *type, struct object_id *oid);
 
-extern int hash_object_file_literally(const void *buf, unsigned long len,
-                                     const char *type, struct object_id *oid,
-                                     unsigned flags);
+int hash_object_file_literally(const void *buf, unsigned long len,
+                              const char *type, struct object_id *oid,
+                              unsigned flags);
 
-extern int pretend_object_file(void *, unsigned long, enum object_type,
-                              struct object_id *oid);
+int pretend_object_file(void *, unsigned long, enum object_type,
+                       struct object_id *oid);
 
-extern int force_object_loose(const struct object_id *oid, time_t mtime);
+int force_object_loose(const struct object_id *oid, time_t mtime);
 
 /*
  * Open the loose object at path, check its hash, and return the contents,
@@ -227,9 +227,9 @@ int repo_has_object_file_with_flags(struct repository *r,
  * with the specified name.  This function does not respect replace
  * references.
  */
-extern int has_loose_object_nonlocal(const struct object_id *);
+int has_loose_object_nonlocal(const struct object_id *);
 
-extern void assert_oid_type(const struct object_id *oid, enum object_type expect);
+void assert_oid_type(const struct object_id *oid, enum object_type expect);
 
 struct object_info {
        /* Request */
index 796792cb32a7ae86b253df05671e1ed4736e4d15..4526979ccf264d10a0fddafcb49697b45fadf71c 100644 (file)
--- a/object.h
+++ b/object.h
@@ -90,19 +90,19 @@ struct object {
        struct object_id oid;
 };
 
-extern const char *type_name(unsigned int type);
-extern int type_from_string_gently(const char *str, ssize_t, int gentle);
+const char *type_name(unsigned int type);
+int type_from_string_gently(const char *str, ssize_t, int gentle);
 #define type_from_string(str) type_from_string_gently(str, -1, 0)
 
 /*
  * Return the current number of buckets in the object hashmap.
  */
-extern unsigned int get_max_object_index(void);
+unsigned int get_max_object_index(void);
 
 /*
  * Return the object from the specified bucket in the object hashmap.
  */
-extern struct object *get_indexed_object(unsigned int);
+struct object *get_indexed_object(unsigned int);
 
 /*
  * This can be used to see if we have heard of the object before, but
@@ -118,7 +118,7 @@ extern struct object *get_indexed_object(unsigned int);
  */
 struct object *lookup_object(struct repository *r, const unsigned char *sha1);
 
-extern void *create_object(struct repository *r, const unsigned char *sha1, void *obj);
+void *create_object(struct repository *r, const unsigned char *sha1, void *obj);
 
 void *object_as_type(struct repository *r, struct object *obj, enum object_type type, int quiet);
 
@@ -189,6 +189,6 @@ void clear_object_flags(unsigned flags);
 /*
  * Clear the specified object flags from all in-core commit objects.
  */
-extern void clear_commit_marks_all(unsigned int flags);
+void clear_commit_marks_all(unsigned int flags);
 
 #endif /* OBJECT_H */
index 72430b611ebf710b4c3ba0160af91104258db138..7a939461ff5baf8aa6d5f599af9e2a5a52a21864 100644 (file)
--- a/oidmap.h
+++ b/oidmap.h
@@ -33,7 +33,7 @@ struct oidmap {
  * parameter may be used to preallocate a sufficiently large table and thus
  * prevent expensive resizing. If 0, the table is dynamically resized.
  */
-extern void oidmap_init(struct oidmap *map, size_t initial_size);
+void oidmap_init(struct oidmap *map, size_t initial_size);
 
 /*
  * Frees an oidmap structure and allocated memory.
@@ -41,13 +41,13 @@ extern void oidmap_init(struct oidmap *map, size_t initial_size);
  * If `free_entries` is true, each oidmap_entry in the map is freed as well
  * using stdlibs free().
  */
-extern void oidmap_free(struct oidmap *map, int free_entries);
+void oidmap_free(struct oidmap *map, int free_entries);
 
 /*
  * Returns the oidmap entry for the specified oid, or NULL if not found.
  */
-extern void *oidmap_get(const struct oidmap *map,
-                       const struct object_id *key);
+void *oidmap_get(const struct oidmap *map,
+                const struct object_id *key);
 
 /*
  * Adds or replaces an oidmap entry.
@@ -57,14 +57,14 @@ extern void *oidmap_get(const struct oidmap *map,
  *
  * Returns the replaced entry, or NULL if not found (i.e. the entry was added).
  */
-extern void *oidmap_put(struct oidmap *map, void *entry);
+void *oidmap_put(struct oidmap *map, void *entry);
 
 /*
  * Removes an oidmap entry matching the specified oid.
  *
  * Returns the removed entry, or NULL if not found.
  */
-extern void *oidmap_remove(struct oidmap *map, const struct object_id *key);
+void *oidmap_remove(struct oidmap *map, const struct object_id *key);
 
 
 struct oidmap_iter {
diff --git a/pack.h b/pack.h
index da99fdd1d2baf7574b606f4c2bc6e1dcfeacf0fa..9fc0945ac9162e542d32c0352ddf6b63e5438f40 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -55,7 +55,7 @@ struct pack_idx_option {
        uint32_t *anomaly;
 };
 
-extern void reset_pack_idx_option(struct pack_idx_option *);
+void reset_pack_idx_option(struct pack_idx_option *);
 
 /*
  * Packed object index header
@@ -79,28 +79,28 @@ struct progress;
 /* Note, the data argument could be NULL if object type is blob */
 typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*);
 
-extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1);
-extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
-extern int verify_pack_index(struct packed_git *);
-extern int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);
-extern off_t write_pack_header(struct hashfile *f, uint32_t);
-extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
-extern char *index_pack_lockfile(int fd);
+const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1);
+int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
+int verify_pack_index(struct packed_git *);
+int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);
+off_t write_pack_header(struct hashfile *f, uint32_t);
+void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
+char *index_pack_lockfile(int fd);
 
 /*
  * The "hdr" output buffer should be at least this big, which will handle sizes
  * up to 2^67.
  */
 #define MAX_PACK_OBJECT_HEADER 10
-extern int encode_in_pack_object_header(unsigned char *hdr, int hdr_len,
-                                       enum object_type, uintmax_t);
+int encode_in_pack_object_header(unsigned char *hdr, int hdr_len,
+                                enum object_type, uintmax_t);
 
 #define PH_ERROR_EOF           (-1)
 #define PH_ERROR_PACK_SIGNATURE        (-2)
 #define PH_ERROR_PROTOCOL      (-3)
-extern int read_pack_header(int fd, struct pack_header *);
+int read_pack_header(int fd, struct pack_header *);
 
-extern struct hashfile *create_tmp_packfile(char **pack_tmp_name);
-extern void finish_tmp_packfile(struct strbuf *name_buffer, const char *pack_tmp_name, struct pack_idx_entry **written_list, uint32_t nr_written, struct pack_idx_option *pack_idx_opts, unsigned char sha1[]);
+struct hashfile *create_tmp_packfile(char **pack_tmp_name);
+void finish_tmp_packfile(struct strbuf *name_buffer, const char *pack_tmp_name, struct pack_idx_entry **written_list, uint32_t nr_written, struct pack_idx_option *pack_idx_opts, unsigned char sha1[]);
 
 #endif
index cdf6b6ec3443cfe3fcabb55a4475a9fc64c8543b..9f52af928131e8bf9f72a1afccdcb0831e6daf9f 100644 (file)
@@ -903,25 +903,25 @@ static void prepare_packed_git(struct repository *r);
  * all unreachable objects about to be pruned, in which case they're not really
  * interesting as a measure of repo size in the first place.
  */
-unsigned long approximate_object_count(void)
+unsigned long repo_approximate_object_count(struct repository *r)
 {
-       if (!the_repository->objects->approximate_object_count_valid) {
+       if (!r->objects->approximate_object_count_valid) {
                unsigned long count;
                struct multi_pack_index *m;
                struct packed_git *p;
 
-               prepare_packed_git(the_repository);
+               prepare_packed_git(r);
                count = 0;
-               for (m = get_multi_pack_index(the_repository); m; m = m->next)
+               for (m = get_multi_pack_index(r); m; m = m->next)
                        count += m->num_objects;
-               for (p = the_repository->objects->packed_git; p; p = p->next) {
+               for (p = r->objects->packed_git; p; p = p->next) {
                        if (open_pack_index(p))
                                continue;
                        count += p->num_objects;
                }
-               the_repository->objects->approximate_object_count = count;
+               r->objects->approximate_object_count = count;
        }
-       return the_repository->objects->approximate_object_count;
+       return r->objects->approximate_object_count;
 }
 
 static void *get_next_packed_git(const void *p)
index 12baa6118a86216d76e342740c592802b70936df..b678d35c0b6df11623f2d29f7b7ea7dfe0e1bea1 100644 (file)
@@ -63,7 +63,8 @@ struct packed_git *get_all_packs(struct repository *r);
  * Give a rough count of objects in the repository. This sacrifices accuracy
  * for speed.
  */
-unsigned long approximate_object_count(void);
+unsigned long repo_approximate_object_count(struct repository *r);
+#define approximate_object_count() repo_approximate_object_count(the_repository)
 
 struct packed_git *find_sha1_pack(const unsigned char *sha1,
                                  struct packed_git *packs);
index 6e2e8d6273a2b3c35248a956f77a19925ded3e7d..4b95d04a37c028322c6fbf7d3f1b59e0d541fdb3 100644 (file)
@@ -96,6 +96,23 @@ int parse_opt_commits(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+int parse_opt_commit(const struct option *opt, const char *arg, int unset)
+{
+       struct object_id oid;
+       struct commit *commit;
+       struct commit **target = opt->value;
+
+       if (!arg)
+               return -1;
+       if (get_oid(arg, &oid))
+               return error("malformed object name %s", arg);
+       commit = lookup_commit_reference(the_repository, &oid);
+       if (!commit)
+               return error("no such commit %s", arg);
+       *target = commit;
+       return 0;
+}
+
 int parse_opt_object_name(const struct option *opt, const char *arg, int unset)
 {
        struct object_id oid;
@@ -112,6 +129,23 @@ int parse_opt_object_name(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+int parse_opt_object_id(const struct option *opt, const char *arg, int unset)
+{
+       struct object_id oid;
+       struct object_id *target = opt->value;
+
+       if (unset) {
+               *target = null_oid;
+               return 0;
+       }
+       if (!arg)
+               return -1;
+       if (get_oid(arg, &oid))
+               return error(_("malformed object name '%s'"), arg);
+       *target = oid;
+       return 0;
+}
+
 int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
 {
        int *target = opt->value;
index cc9230adacb61f2a5066ac094c66bdfd18534354..bd00cf004900c478a0dfae2f0d311d356a67b3c6 100644 (file)
@@ -277,8 +277,12 @@ int parse_opt_abbrev_cb(const struct option *, const char *, int);
 int parse_opt_expiry_date_cb(const struct option *, const char *, int);
 int parse_opt_color_flag_cb(const struct option *, const char *, int);
 int parse_opt_verbosity_cb(const struct option *, const char *, int);
+/* value is struct oid_array* */
 int parse_opt_object_name(const struct option *, const char *, int);
+/* value is struct object_id* */
+int parse_opt_object_id(const struct option *, const char *, int);
 int parse_opt_commits(const struct option *, const char *, int);
+int parse_opt_commit(const struct option *, const char *, int);
 int parse_opt_tertiary(const struct option *, const char *, int);
 int parse_opt_string_list(const struct option *, const char *, int);
 int parse_opt_noop_cb(const struct option *, const char *, int);
@@ -316,5 +320,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);
 #define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG)
 #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
 #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
+#define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
 
 #endif
diff --git a/path.h b/path.h
index 651e6157fc480264e3495bd508a88d0835de1379..2ba6ca58c83487b5e02a71ce7c2f0d556c59aebe 100644 (file)
--- a/path.h
+++ b/path.h
@@ -13,19 +13,19 @@ struct strbuf;
 /*
  * Return a statically allocated path.
  */
-extern const char *mkpath(const char *fmt, ...)
+const char *mkpath(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
 
 /*
  * Return a path.
  */
-extern char *mkpathdup(const char *fmt, ...)
+char *mkpathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
 
 /*
  * Construct a path and place the result in the provided buffer `buf`.
  */
-extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
+char *mksnpath(char *buf, size_t n, const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
 
 /*
@@ -37,16 +37,16 @@ extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
  * Constructs a path into the common git directory of repository `repo` and
  * append it in the provided buffer `sb`.
  */
-extern void strbuf_git_common_path(struct strbuf *sb,
-                                  const struct repository *repo,
-                                  const char *fmt, ...)
+void strbuf_git_common_path(struct strbuf *sb,
+                           const struct repository *repo,
+                           const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
 
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) common git directory.
  */
-extern const char *git_common_path(const char *fmt, ...)
+const char *git_common_path(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
 
 
@@ -66,30 +66,30 @@ extern const char *git_common_path(const char *fmt, ...)
 /*
  * Return a path into the git directory of repository `repo`.
  */
-extern char *repo_git_path(const struct repository *repo,
-                          const char *fmt, ...)
+char *repo_git_path(const struct repository *repo,
+                   const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 
 /*
  * Construct a path into the git directory of repository `repo` and append it
  * to the provided buffer `sb`.
  */
-extern void strbuf_repo_git_path(struct strbuf *sb,
-                                const struct repository *repo,
-                                const char *fmt, ...)
+void strbuf_repo_git_path(struct strbuf *sb,
+                         const struct repository *repo,
+                         const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
 
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) git directory.
  */
-extern const char *git_path(const char *fmt, ...)
+const char *git_path(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
 
 /*
  * Return a path into the main repository's (the_repository) git directory.
  */
-extern char *git_pathdup(const char *fmt, ...)
+char *git_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
 
 /*
@@ -97,14 +97,14 @@ extern char *git_pathdup(const char *fmt, ...)
  * and place it in the provided buffer `buf`, the contents of the buffer will
  * be overridden.
  */
-extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
+char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 
 /*
  * Construct a path into the main repository's (the_repository) git directory
  * and append it to the provided buffer `sb`.
  */
-extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
+void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 
 /*
@@ -112,7 +112,7 @@ extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
  *
  * If the repository doesn't have a worktree NULL is returned.
  */
-extern char *repo_worktree_path(const struct repository *repo,
+char *repo_worktree_path(const struct repository *repo,
                                const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 
@@ -122,7 +122,7 @@ extern char *repo_worktree_path(const struct repository *repo,
  *
  * If the repository doesn't have a worktree nothing will be appended to `sb`.
  */
-extern void strbuf_repo_worktree_path(struct strbuf *sb,
+void strbuf_repo_worktree_path(struct strbuf *sb,
                                      const struct repository *repo,
                                      const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
@@ -131,7 +131,7 @@ extern void strbuf_repo_worktree_path(struct strbuf *sb,
  * Return a path into a submodule's git directory located at `path`.  `path`
  * must only reference a submodule of the main repository (the_repository).
  */
-extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
+char *git_pathdup_submodule(const char *path, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 
 /*
@@ -139,11 +139,11 @@ extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
  * append it to the provided buffer `sb`.  `path` must only reference a
  * submodule of the main repository (the_repository).
  */
-extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
+int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
                                     const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
 
-extern void report_linked_checkout_garbage(void);
+void report_linked_checkout_garbage(void);
 
 /*
  * You can define a static memoized git path like:
index c36cb788ed47f388e9c51fca46cb6ec4e73830ef..5c62015db4df7007d6c0f5a1cc6041ce04e14b43 100644 (file)
@@ -172,9 +172,9 @@ struct packet_reader {
  * Initialize a 'struct packet_reader' object which is an
  * abstraction around the 'packet_read_with_status()' function.
  */
-extern void packet_reader_init(struct packet_reader *reader, int fd,
-                              char *src_buffer, size_t src_len,
-                              int options);
+void packet_reader_init(struct packet_reader *reader, int fd,
+                       char *src_buffer, size_t src_len,
+                       int options);
 
 /*
  * Perform a packet read and return the status of the read.
@@ -186,7 +186,7 @@ extern void packet_reader_init(struct packet_reader *reader, int fd,
  *                    'line' is set to point at the read line
  * PACKET_READ_FLUSH: 'pktlen' is set to '0' and 'line' is set to NULL
  */
-extern enum packet_read_status packet_reader_read(struct packet_reader *reader);
+enum packet_read_status packet_reader_read(struct packet_reader *reader);
 
 /*
  * Peek the next packet line without consuming it and return the status.
@@ -196,7 +196,7 @@ extern enum packet_read_status packet_reader_read(struct packet_reader *reader);
  * Peeking multiple times without calling 'packet_reader_read()' will return
  * the same result.
  */
-extern enum packet_read_status packet_reader_peek(struct packet_reader *reader);
+enum packet_read_status packet_reader_peek(struct packet_reader *reader);
 
 #define DEFAULT_PACKET_MAX 1000
 #define LARGE_PACKET_MAX 65520
index ec6a1926d4465e61364a7a8450e8f4c86ed841f0..1b705cee1fe95150c8cce73a5cced934aea6cffd 100644 (file)
@@ -10,8 +10,8 @@
 #include <string.h>
 #include "sha1.h"
 
-extern void ppc_sha1_core(uint32_t *hash, const unsigned char *p,
-                         unsigned int nblocks);
+void ppc_sha1_core(uint32_t *hash, const unsigned char *p,
+                  unsigned int nblocks);
 
 int ppc_SHA1_Init(ppc_SHA_CTX *c)
 {
index 682e51867a3452bc1106bc63f44d6d6ba5f523b9..4f9a37e6bee5b537c10458c9ec18792cfd5de348 100644 (file)
@@ -37,24 +37,24 @@ struct prio_queue {
 /*
  * Add the "thing" to the queue.
  */
-extern void prio_queue_put(struct prio_queue *, void *thing);
+void prio_queue_put(struct prio_queue *, void *thing);
 
 /*
  * Extract the "thing" that compares the smallest out of the queue,
  * or NULL.  If compare function is NULL, the queue acts as a LIFO
  * stack.
  */
-extern void *prio_queue_get(struct prio_queue *);
+void *prio_queue_get(struct prio_queue *);
 
 /*
  * Gain access to the "thing" that would be returned by
  * prio_queue_get, but do not remove it from the queue.
  */
-extern void *prio_queue_peek(struct prio_queue *);
+void *prio_queue_peek(struct prio_queue *);
 
-extern void clear_prio_queue(struct prio_queue *);
+void clear_prio_queue(struct prio_queue *);
 
 /* Reverse the LIFO elements */
-extern void prio_queue_reverse(struct prio_queue *);
+void prio_queue_reverse(struct prio_queue *);
 
 #endif /* PRIO_QUEUE_H */
index 2ad35e433c1e6f5f0d08c9d7500dc35782429f51..cef1a4a01c7902b0b1181a43580639fcead442ff 100644 (file)
@@ -14,7 +14,7 @@ enum protocol_version {
  * 'protocol.version' config.  If unconfigured, a value of 'protocol_v0' is
  * returned.
  */
-extern enum protocol_version get_protocol_version_config(void);
+enum protocol_version get_protocol_version_config(void);
 
 /*
  * Used by a server to determine which protocol version should be used based on
@@ -23,12 +23,12 @@ extern enum protocol_version get_protocol_version_config(void);
  * request a particular protocol version, a default of 'protocol_v0' will be
  * used.
  */
-extern enum protocol_version determine_protocol_version_server(void);
+enum protocol_version determine_protocol_version_server(void);
 
 /*
  * Used by a client to determine which protocol version the server is speaking
  * based on the server's initial response.
  */
-extern enum protocol_version determine_protocol_version_client(const char *server_response);
+enum protocol_version determine_protocol_version_client(const char *server_response);
 
 #endif /* PROTOCOL_H */
diff --git a/quote.h b/quote.h
index ea992dcc91ef599b5d053b00ae3a036ce5e87c1a..fb08dc085cca25c276cebd79a4cac5b81bd6ad9a 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -29,9 +29,9 @@ struct strbuf;
  * sq_quotef() quotes the entire formatted string as a single result.
  */
 
-extern void sq_quote_buf(struct strbuf *, const char *src);
-extern void sq_quote_argv(struct strbuf *, const char **argv);
-extern void sq_quotef(struct strbuf *, const char *fmt, ...);
+void sq_quote_buf(struct strbuf *, const char *src);
+void sq_quote_argv(struct strbuf *, const char **argv);
+void sq_quotef(struct strbuf *, const char *fmt, ...);
 
 /*
  * These match their non-pretty variants, except that they avoid
@@ -45,14 +45,14 @@ void sq_quote_argv_pretty(struct strbuf *, const char **argv);
  * NULL if the input does not look like what sq_quote would have
  * produced.
  */
-extern char *sq_dequote(char *);
+char *sq_dequote(char *);
 
 /*
  * Same as the above, but can be used to unwrap many arguments in the
  * same string separated by space. Like sq_quote, it works in place,
  * modifying arg and appending pointers into it to argv.
  */
-extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
+int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
 
 /*
  * Same as above, but store the unquoted strings in an argv_array. We will
@@ -60,24 +60,24 @@ extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc
  * will duplicate and take ownership of the strings.
  */
 struct argv_array;
-extern int sq_dequote_to_argv_array(char *arg, struct argv_array *);
+int sq_dequote_to_argv_array(char *arg, struct argv_array *);
 
-extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
-extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
-extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
+int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
+size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
+void quote_two_c_style(struct strbuf *, const char *, const char *, int);
 
-extern void write_name_quoted(const char *name, FILE *, int terminator);
-extern void write_name_quoted_relative(const char *name, const char *prefix,
-               FILE *fp, int terminator);
+void write_name_quoted(const char *name, FILE *, int terminator);
+void write_name_quoted_relative(const char *name, const char *prefix,
+                               FILE *fp, int terminator);
 
 /* quote path as relative to the given prefix */
-extern char *quote_path_relative(const char *in, const char *prefix,
+char *quote_path_relative(const char *in, const char *prefix,
                          struct strbuf *out);
 
 /* quoting as a string literal for other languages */
-extern void perl_quote_buf(struct strbuf *sb, const char *src);
-extern void python_quote_buf(struct strbuf *sb, const char *src);
-extern void tcl_quote_buf(struct strbuf *sb, const char *src);
-extern void basic_regex_quote_buf(struct strbuf *sb, const char *src);
+void perl_quote_buf(struct strbuf *sb, const char *src);
+void python_quote_buf(struct strbuf *sb, const char *src);
+void tcl_quote_buf(struct strbuf *sb, const char *src);
+void basic_regex_quote_buf(struct strbuf *sb, const char *src);
 
 #endif
index 18b0f9f2f0df0b6cceb67bc0eee81244e3c8a114..5df932ad8f54418927d30117ee0209ba91459bde 100644 (file)
@@ -4,9 +4,9 @@
 struct progress;
 struct rev_info;
 
-extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-                                                 timestamp_t timestamp);
-extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-                                  timestamp_t mark_recent, struct progress *);
+int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
+                                          timestamp_t timestamp);
+void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
+                           timestamp_t mark_recent, struct progress *);
 
 #endif
index d5a74b1861eb66861f2394f477775320efb07be7..4fad4e3f9ab004c85c0b2614b4d0637809d7e1b8 100644 (file)
@@ -709,6 +709,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
                          (intent_only ? ADD_CACHE_NEW_ONLY : 0));
        int hash_flags = HASH_WRITE_OBJECT;
+       struct object_id oid;
 
        if (flags & ADD_CACHE_RENORMALIZE)
                hash_flags |= HASH_RENORMALIZE;
@@ -718,6 +719,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
 
        namelen = strlen(path);
        if (S_ISDIR(st_mode)) {
+               if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+                       return error(_("'%s' does not have a commit checked out"), path);
                while (namelen && path[namelen-1] == '/')
                        namelen--;
        }
@@ -3128,7 +3131,7 @@ static int write_shared_index(struct index_state *istate,
        trace2_region_enter_printf("index", "shared/do_write_index",
                                   the_repository, "%s", (*temp)->filename.buf);
        ret = do_write_index(si->base, *temp, 1);
-       trace2_region_enter_printf("index", "shared/do_write_index",
+       trace2_region_leave_printf("index", "shared/do_write_index",
                                   the_repository, "%s", (*temp)->filename.buf);
 
        if (ret)
index 8d11a94cbd40d89302ec8b03245744f00bc02abf..8500671bc60957432568443de42b548612106f3c 100644 (file)
@@ -1392,7 +1392,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                *s = show_ref(&atom->u.remote_ref.refname, refname);
        else if (atom->u.remote_ref.option == RR_TRACK) {
                if (stat_tracking_info(branch, &num_ours, &num_theirs,
-                                      NULL, AHEAD_BEHIND_FULL) < 0) {
+                                      NULL, atom->u.remote_ref.push,
+                                      AHEAD_BEHIND_FULL) < 0) {
                        *s = xstrdup(msgs.gone);
                } else if (!num_ours && !num_theirs)
                        *s = xstrdup("");
@@ -1410,7 +1411,8 @@ 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, atom->u.remote_ref.push,
+                                      AHEAD_BEHIND_FULL) < 0) {
                        *s = xstrdup("");
                        return;
                }
index cb3e73755d445b866ad51246dc8b1ea3ab6f1639..f26408f6cc1caffd93949ebc95d74426c2a9db25 100644 (file)
@@ -6,21 +6,21 @@
 struct commit;
 struct reflog_walk_info;
 
-extern void init_reflog_walk(struct reflog_walk_info **info);
-extern int add_reflog_for_walk(struct reflog_walk_info *info,
-               struct commit *commit, const char *name);
-extern void show_reflog_message(struct reflog_walk_info *info, int,
-                               const struct date_mode *, int force_date);
-extern void get_reflog_message(struct strbuf *sb,
-               struct reflog_walk_info *reflog_info);
-extern const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
-extern timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info);
-extern void get_reflog_selector(struct strbuf *sb,
-               struct reflog_walk_info *reflog_info,
-               const struct date_mode *dmode, int force_date,
-               int shorten);
+void init_reflog_walk(struct reflog_walk_info **info);
+int add_reflog_for_walk(struct reflog_walk_info *info,
+                       struct commit *commit, const char *name);
+void show_reflog_message(struct reflog_walk_info *info, int,
+                        const struct date_mode *, int force_date);
+void get_reflog_message(struct strbuf *sb,
+                       struct reflog_walk_info *reflog_info);
+const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
+timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info);
+void get_reflog_selector(struct strbuf *sb,
+                        struct reflog_walk_info *reflog_info,
+                        const struct date_mode *dmode, int force_date,
+                        int shorten);
 
-extern int reflog_walk_empty(struct reflog_walk_info *walk);
+int reflog_walk_empty(struct reflog_walk_info *walk);
 
 struct commit *next_reflog_entry(struct reflog_walk_info *reflog_info);
 
diff --git a/refs.c b/refs.c
index 142888a40a611370240864e2f27f097832db11e9..92d1f6dbdd0d313cdf109e02f31a51bb55cfc601 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -241,9 +241,14 @@ int read_ref(const char *refname, struct object_id *oid)
        return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL);
 }
 
+static int refs_ref_exists(struct ref_store *refs, const char *refname)
+{
+       return !!refs_resolve_ref_unsafe(refs, refname, RESOLVE_REF_READING, NULL, NULL);
+}
+
 int ref_exists(const char *refname)
 {
-       return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, NULL);
+       return refs_ref_exists(get_main_ref_store(the_repository), refname);
 }
 
 static int match_ref_pattern(const char *refname,
@@ -534,10 +539,11 @@ void expand_ref_prefix(struct argv_array *prefixes, const char *prefix)
  * later free()ing) if the string passed in is a magic short-hand form
  * to name a branch.
  */
-static char *substitute_branch_name(const char **string, int *len)
+static char *substitute_branch_name(struct repository *r,
+                                   const char **string, int *len)
 {
        struct strbuf buf = STRBUF_INIT;
-       int ret = interpret_branch_name(*string, *len, &buf, 0);
+       int ret = repo_interpret_branch_name(r, *string, *len, &buf, 0);
 
        if (ret == *len) {
                size_t size;
@@ -549,15 +555,22 @@ static char *substitute_branch_name(const char **string, int *len)
        return NULL;
 }
 
-int dwim_ref(const char *str, int len, struct object_id *oid, char **ref)
+int repo_dwim_ref(struct repository *r, const char *str, int len,
+                 struct object_id *oid, char **ref)
 {
-       char *last_branch = substitute_branch_name(&str, &len);
-       int   refs_found  = expand_ref(str, len, oid, ref);
+       char *last_branch = substitute_branch_name(r, &str, &len);
+       int   refs_found  = expand_ref(r, str, len, oid, ref);
        free(last_branch);
        return refs_found;
 }
 
-int expand_ref(const char *str, int len, struct object_id *oid, char **ref)
+int dwim_ref(const char *str, int len, struct object_id *oid, char **ref)
+{
+       return repo_dwim_ref(the_repository, str, len, oid, ref);
+}
+
+int expand_ref(struct repository *repo, const char *str, int len,
+              struct object_id *oid, char **ref)
 {
        const char **p, *r;
        int refs_found = 0;
@@ -572,8 +585,9 @@ int expand_ref(const char *str, int len, struct object_id *oid, char **ref)
                this_result = refs_found ? &oid_from_ref : oid;
                strbuf_reset(&fullref);
                strbuf_addf(&fullref, *p, len, str);
-               r = resolve_ref_unsafe(fullref.buf, RESOLVE_REF_READING,
-                                      this_result, &flag);
+               r = refs_resolve_ref_unsafe(get_main_ref_store(repo),
+                                           fullref.buf, RESOLVE_REF_READING,
+                                           this_result, &flag);
                if (r) {
                        if (!refs_found++)
                                *ref = xstrdup(r);
@@ -589,9 +603,11 @@ int expand_ref(const char *str, int len, struct object_id *oid, char **ref)
        return refs_found;
 }
 
-int dwim_log(const char *str, int len, struct object_id *oid, char **log)
+int repo_dwim_log(struct repository *r, const char *str, int len,
+                 struct object_id *oid, char **log)
 {
-       char *last_branch = substitute_branch_name(&str, &len);
+       struct ref_store *refs = get_main_ref_store(r);
+       char *last_branch = substitute_branch_name(r, &str, &len);
        const char **p;
        int logs_found = 0;
        struct strbuf path = STRBUF_INIT;
@@ -603,13 +619,15 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)
 
                strbuf_reset(&path);
                strbuf_addf(&path, *p, len, str);
-               ref = resolve_ref_unsafe(path.buf, RESOLVE_REF_READING,
-                                        &hash, NULL);
+               ref = refs_resolve_ref_unsafe(refs, path.buf,
+                                             RESOLVE_REF_READING,
+                                             &hash, NULL);
                if (!ref)
                        continue;
-               if (reflog_exists(path.buf))
+               if (refs_reflog_exists(refs, path.buf))
                        it = path.buf;
-               else if (strcmp(ref, path.buf) && reflog_exists(ref))
+               else if (strcmp(ref, path.buf) &&
+                        refs_reflog_exists(refs, ref))
                        it = ref;
                else
                        continue;
@@ -625,6 +643,11 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)
        return logs_found;
 }
 
+int dwim_log(const char *str, int len, struct object_id *oid, char **log)
+{
+       return repo_dwim_log(the_repository, str, len, oid, log);
+}
+
 static int is_per_worktree_ref(const char *refname)
 {
        return !strcmp(refname, "HEAD") ||
@@ -944,7 +967,8 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
        return 1;
 }
 
-int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
+int read_ref_at(struct ref_store *refs, const char *refname,
+               unsigned int flags, timestamp_t at_time, int cnt,
                struct object_id *oid, char **msg,
                timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
@@ -960,7 +984,7 @@ int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, in
        cb.cutoff_cnt = cutoff_cnt;
        cb.oid = oid;
 
-       for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
+       refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent, &cb);
 
        if (!cb.reccnt) {
                if (flags & GET_OID_QUIETLY)
@@ -971,7 +995,7 @@ int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, in
        if (cb.found_it)
                return 0;
 
-       for_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb);
+       refs_for_each_reflog_ent(refs, refname, read_ref_at_ent_oldest, &cb);
 
        return 1;
 }
@@ -1164,7 +1188,8 @@ int update_ref(const char *msg, const char *refname,
                               old_oid, flags, onerr);
 }
 
-char *shorten_unambiguous_ref(const char *refname, int strict)
+char *refs_shorten_unambiguous_ref(struct ref_store *refs,
+                                  const char *refname, int strict)
 {
        int i;
        static char **scanf_fmts;
@@ -1242,7 +1267,7 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                        strbuf_reset(&resolved_buf);
                        strbuf_addf(&resolved_buf, rule,
                                    short_name_len, short_name);
-                       if (ref_exists(resolved_buf.buf))
+                       if (refs_ref_exists(refs, resolved_buf.buf))
                                break;
                }
 
@@ -1261,6 +1286,12 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
        return xstrdup(refname);
 }
 
+char *shorten_unambiguous_ref(const char *refname, int strict)
+{
+       return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+                                           refname, strict);
+}
+
 static struct string_list *hide_refs;
 
 int parse_hide_refs_config(const char *var, const char *value, const char *section)
diff --git a/refs.h b/refs.h
index 308fa1f03b26c151f2df76a0c4f2f675e7bbee5a..2727405b61c4e26538c3e76b528df37078337376 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -111,7 +111,7 @@ int should_autocreate_reflog(const char *refname);
 
 int is_branch(const char *refname);
 
-extern int refs_init_db(struct strbuf *err);
+int refs_init_db(struct strbuf *err);
 
 /*
  * If refname is a non-symbolic reference that refers to a tag object,
@@ -148,7 +148,9 @@ int refname_match(const char *abbrev_name, const char *full_name);
 struct argv_array;
 void expand_ref_prefix(struct argv_array *prefixes, const char *prefix);
 
-int expand_ref(const char *str, int len, struct object_id *oid, char **ref);
+int expand_ref(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
+int repo_dwim_ref(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
+int repo_dwim_log(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
 int dwim_ref(const char *str, int len, struct object_id *oid, char **ref);
 int dwim_log(const char *str, int len, struct object_id *oid, char **ref);
 
@@ -386,7 +388,8 @@ int refs_create_reflog(struct ref_store *refs, const char *refname,
 int safe_create_reflog(const char *refname, int force_create, struct strbuf *err);
 
 /** Reads log for the value of ref during at_time. **/
-int read_ref_at(const char *refname, unsigned int flags,
+int read_ref_at(struct ref_store *refs,
+               const char *refname, unsigned int flags,
                timestamp_t at_time, int cnt,
                struct object_id *oid, char **msg,
                timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
@@ -462,6 +465,8 @@ int check_refname_format(const char *refname, int flags);
 
 const char *prettify_refname(const char *refname);
 
+char *refs_shorten_unambiguous_ref(struct ref_store *refs,
+                                  const char *refname, int strict);
 char *shorten_unambiguous_ref(const char *refname, int strict);
 
 /** rename ref, return 0 on success **/
index 3fe34eae853851f16271e0500b827ef855c30f14..e50f7602eda56e0fb9deeedcd9d3d4e0a800a822 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1880,37 +1880,27 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)
 }
 
 /*
- * Lookup the upstream branch for the given branch and if present, optionally
- * compute the commit ahead/behind values for the pair.
+ * Compute the commit ahead/behind values for the pair branch_name, base.
  *
  * If abf is AHEAD_BEHIND_FULL, compute the full ahead/behind and return the
  * counts in *num_ours and *num_theirs.  If abf is AHEAD_BEHIND_QUICK, skip
  * the (potentially expensive) a/b computation (*num_ours and *num_theirs are
  * set to zero).
  *
- * The name of the upstream branch (or NULL if no upstream is defined) is
- * returned via *upstream_name, if it is not itself NULL.
- *
- * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no
- * upstream defined, or ref does not exist).  Returns 0 if the commits are
- * identical.  Returns 1 if commits are different.
+ * Returns -1 if num_ours and num_theirs could not be filled in (e.g., ref
+ * does not exist).  Returns 0 if the commits are identical.  Returns 1 if
+ * commits are different.
  */
-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
-                      const char **upstream_name, enum ahead_behind_flags abf)
+
+static int stat_branch_pair(const char *branch_name, const char *base,
+                            int *num_ours, int *num_theirs,
+                            enum ahead_behind_flags abf)
 {
        struct object_id oid;
        struct commit *ours, *theirs;
        struct rev_info revs;
-       const char *base;
        struct argv_array argv = ARGV_ARRAY_INIT;
 
-       /* Cannot stat unless we are marked to build on top of somebody else. */
-       base = branch_get_upstream(branch, NULL);
-       if (upstream_name)
-               *upstream_name = base;
-       if (!base)
-               return -1;
-
        /* Cannot stat if what we used to build on no longer exists */
        if (read_ref(base, &oid))
                return -1;
@@ -1918,7 +1908,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
        if (!theirs)
                return -1;
 
-       if (read_ref(branch->refname, &oid))
+       if (read_ref(branch_name, &oid))
                return -1;
        ours = lookup_commit_reference(the_repository, &oid);
        if (!ours)
@@ -1932,7 +1922,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
        if (abf == AHEAD_BEHIND_QUICK)
                return 1;
        if (abf != AHEAD_BEHIND_FULL)
-               BUG("stat_tracking_info: invalid abf '%d'", abf);
+               BUG("stat_branch_pair: invalid abf '%d'", abf);
 
        /* Run "rev-list --left-right ours...theirs" internally... */
        argv_array_push(&argv, ""); /* ignored */
@@ -1966,6 +1956,42 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
        return 1;
 }
 
+/*
+ * Lookup the tracking branch for the given branch and if present, optionally
+ * compute the commit ahead/behind values for the pair.
+ *
+ * If for_push is true, the tracking branch refers to the push branch,
+ * otherwise it refers to the upstream branch.
+ *
+ * The name of the tracking branch (or NULL if it is not defined) is
+ * returned via *tracking_name, if it is not itself NULL.
+ *
+ * If abf is AHEAD_BEHIND_FULL, compute the full ahead/behind and return the
+ * counts in *num_ours and *num_theirs.  If abf is AHEAD_BEHIND_QUICK, skip
+ * the (potentially expensive) a/b computation (*num_ours and *num_theirs are
+ * set to zero).
+ *
+ * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no
+ * upstream defined, or ref does not exist).  Returns 0 if the commits are
+ * identical.  Returns 1 if commits are different.
+ */
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
+                      const char **tracking_name, int for_push,
+                      enum ahead_behind_flags abf)
+{
+       const char *base;
+
+       /* Cannot stat unless we are marked to build on top of somebody else. */
+       base = for_push ? branch_get_push(branch, NULL) :
+               branch_get_upstream(branch, NULL);
+       if (tracking_name)
+               *tracking_name = base;
+       if (!base)
+               return -1;
+
+       return stat_branch_pair(branch->refname, base, num_ours, num_theirs, abf);
+}
+
 /*
  * Return true when there is anything to report, otherwise false.
  */
@@ -1977,7 +2003,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
        char *base;
        int upstream_is_gone = 0;
 
-       sti = stat_tracking_info(branch, &ours, &theirs, &full_base, abf);
+       sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
        if (sti < 0) {
                if (!full_base)
                        return 0;
index f58332a27e96ee9e3ec7bea53e7889d62a0140a2..83e885672ba968309c17341b2067f6863acfaaf9 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -119,13 +119,13 @@ struct ref {
 #define REF_HEADS      (1u << 1)
 #define REF_TAGS       (1u << 2)
 
-extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
+struct ref *find_ref_by_name(const struct ref *list, const char *name);
 
 struct ref *alloc_ref(const char *name);
 struct ref *copy_ref(const struct ref *ref);
 struct ref *copy_ref_list(const struct ref *ref);
 void sort_ref_list(struct ref **, int (*cmp)(const void *, const void *));
-extern int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref);
+int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref);
 int ref_compare_name(const void *, const void *);
 
 int check_ref_type(const struct ref *ref, int flags);
@@ -141,16 +141,16 @@ struct oid_array;
 struct packet_reader;
 struct argv_array;
 struct string_list;
-extern struct ref **get_remote_heads(struct packet_reader *reader,
-                                    struct ref **list, unsigned int flags,
-                                    struct oid_array *extra_have,
-                                    struct oid_array *shallow_points);
+struct ref **get_remote_heads(struct packet_reader *reader,
+                             struct ref **list, unsigned int flags,
+                             struct oid_array *extra_have,
+                             struct oid_array *shallow_points);
 
 /* Used for protocol v2 in order to retrieve refs from a remote */
-extern struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
-                                   struct ref **list, int for_push,
-                                   const struct argv_array *ref_prefixes,
-                                   const struct string_list *server_options);
+struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
+                            struct ref **list, int for_push,
+                            const struct argv_array *ref_prefixes,
+                            const struct string_list *server_options);
 
 int resolve_remote_symref(struct ref *ref, struct ref *list);
 
@@ -255,7 +255,8 @@ enum ahead_behind_flags {
 
 /* Reporting of tracking info */
 int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
-                      const char **upstream_name, enum ahead_behind_flags abf);
+                      const char **upstream_name, int for_push,
+                      enum ahead_behind_flags abf);
 int format_tracking_info(struct branch *branch, struct strbuf *sb,
                         enum ahead_behind_flags abf);
 
@@ -289,9 +290,9 @@ struct push_cas_option {
        int alloc;
 };
 
-extern int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
+int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
 
-extern int is_empty_cas(const struct push_cas_option *);
+int is_empty_cas(const struct push_cas_option *);
 void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
 
 #endif
index 16528df942f79e3d7ce65d99e0103395d0c69f51..04ed7a85a2402d64e715c38850a41bea43d0f3b3 100644 (file)
@@ -16,8 +16,8 @@ void prepare_replace_object(struct repository *r);
  * This internal function is only declared here for the benefit of
  * lookup_replace_object().  Please do not call it directly.
  */
-extern const struct object_id *do_lookup_replace_object(struct repository *r,
-                                                       const struct object_id *oid);
+const struct object_id *do_lookup_replace_object(struct repository *r,
+                                                const struct object_id *oid);
 
 /*
  * If object sha1 should be replaced, return the replacement object's
index fbe348efac929f9202c4f3e81d962e84157b9082..2b3f0f901e63aeab70dfca86d3067d169dc315e2 100644 (file)
@@ -8,12 +8,12 @@ struct resolve_undo_info {
        struct object_id oid[3];
 };
 
-extern void record_resolve_undo(struct index_state *, struct cache_entry *);
-extern void resolve_undo_write(struct strbuf *, struct string_list *);
-extern struct string_list *resolve_undo_read(const char *, unsigned long);
-extern void resolve_undo_clear_index(struct index_state *);
-extern int unmerge_index_entry_at(struct index_state *, int);
-extern void unmerge_index(struct index_state *, const struct pathspec *);
-extern void unmerge_marked_index(struct index_state *);
+void record_resolve_undo(struct index_state *, struct cache_entry *);
+void resolve_undo_write(struct strbuf *, struct string_list *);
+struct string_list *resolve_undo_read(const char *, unsigned long);
+void resolve_undo_clear_index(struct index_state *);
+int unmerge_index_entry_at(struct index_state *, int);
+void unmerge_index(struct index_state *, const struct pathspec *);
+void unmerge_marked_index(struct index_state *);
 
 #endif
index a6950691c042e6234b46c0b8c94f5312da58f734..f769e03f018f3ca20d7c17f1c5d021555a91f626 100644 (file)
@@ -55,7 +55,7 @@ struct child_process {
 #define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT }
 void child_process_init(struct child_process *);
 void child_process_clear(struct child_process *);
-extern int is_executable(const char *name);
+int is_executable(const char *name);
 
 int start_command(struct child_process *);
 int finish_command(struct child_process *);
@@ -67,10 +67,10 @@ int run_command(struct child_process *);
  * or disabled. Note that this points to static storage that will be
  * overwritten by further calls to find_hook and run_hook_*.
  */
-extern const char *find_hook(const char *name);
+const char *find_hook(const char *name);
 LAST_ARG_MUST_BE_NULL
-extern int run_hook_le(const char *const *env, const char *name, ...);
-extern int run_hook_ve(const char *const *env, const char *name, va_list args);
+int run_hook_le(const char *const *env, const char *name, ...);
+int run_hook_ve(const char *const *env, const char *name, va_list args);
 
 #define RUN_COMMAND_NO_STDIN 1
 #define RUN_GIT_CMD         2  /*If this is to be git sub-command */
index 546f28189851875b4a881ef9ff8876528e5dc9f9..f88a97fb10a322c21062ec8c102482677cfd0feb 100644 (file)
@@ -171,17 +171,22 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)
                if (status)
                        return status;
 
-               if (!strcmp(s, "verbatim"))
+               if (!strcmp(s, "verbatim")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
-               else if (!strcmp(s, "whitespace"))
+                       opts->explicit_cleanup = 1;
+               } else if (!strcmp(s, "whitespace")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
-               else if (!strcmp(s, "strip"))
+                       opts->explicit_cleanup = 1;
+               } else if (!strcmp(s, "strip")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
-               else if (!strcmp(s, "scissors"))
-                       opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
-               else
+                       opts->explicit_cleanup = 1;
+               } else if (!strcmp(s, "scissors")) {
+                       opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;
+                       opts->explicit_cleanup = 1;
+               } else {
                        warning(_("invalid commit message cleanup mode '%s'"),
                                  s);
+               }
 
                free((char *)s);
                return status;
@@ -510,11 +515,54 @@ static int fast_forward_to(struct repository *r,
        return 0;
 }
 
+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
+       int use_editor)
+{
+       if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
+               return use_editor ? COMMIT_MSG_CLEANUP_ALL :
+                                   COMMIT_MSG_CLEANUP_SPACE;
+       else if (!strcmp(cleanup_arg, "verbatim"))
+               return COMMIT_MSG_CLEANUP_NONE;
+       else if (!strcmp(cleanup_arg, "whitespace"))
+               return COMMIT_MSG_CLEANUP_SPACE;
+       else if (!strcmp(cleanup_arg, "strip"))
+               return COMMIT_MSG_CLEANUP_ALL;
+       else if (!strcmp(cleanup_arg, "scissors"))
+               return use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
+                                   COMMIT_MSG_CLEANUP_SPACE;
+       else
+               die(_("Invalid cleanup mode %s"), cleanup_arg);
+}
+
+/*
+ * NB using int rather than enum cleanup_mode to stop clang's
+ * -Wtautological-constant-out-of-range-compare complaining that the comparison
+ * is always true.
+ */
+static const char *describe_cleanup_mode(int cleanup_mode)
+{
+       static const char *modes[] = { "whitespace",
+                                      "verbatim",
+                                      "scissors",
+                                      "strip" };
+
+       if (cleanup_mode < ARRAY_SIZE(modes))
+               return modes[cleanup_mode];
+
+       BUG("invalid cleanup_mode provided (%d)", cleanup_mode);
+}
+
 void append_conflicts_hint(struct index_state *istate,
-                          struct strbuf *msgbuf)
+       struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)
 {
        int i;
 
+       if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+               strbuf_addch(msgbuf, '\n');
+               wt_status_append_cut_line(msgbuf);
+               strbuf_addch(msgbuf, comment_line_char);
+       }
+
        strbuf_addch(msgbuf, '\n');
        strbuf_commented_addf(msgbuf, "Conflicts:\n");
        for (i = 0; i < istate->cache_nr;) {
@@ -582,7 +630,8 @@ static int do_recursive_merge(struct repository *r,
                        _(action_name(opts)));
 
        if (!clean)
-               append_conflicts_hint(r->index, msgbuf);
+               append_conflicts_hint(r->index, msgbuf,
+                                     opts->default_msg_cleanup);
 
        return !clean;
 }
@@ -901,7 +950,6 @@ static int run_git_commit(struct repository *r,
                          unsigned int flags)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
-       const char *value;
 
        if ((flags & CREATE_ROOT_COMMIT) && !(flags & AMEND_MSG)) {
                struct strbuf msg = STRBUF_INIT, script = STRBUF_INIT;
@@ -971,7 +1019,7 @@ static int run_git_commit(struct repository *r,
                argv_array_push(&cmd.args, "-e");
        else if (!(flags & CLEANUP_MSG) &&
                 !opts->signoff && !opts->record_origin &&
-                git_config_get_value("commit.cleanup", &value))
+                !opts->explicit_cleanup)
                argv_array_push(&cmd.args, "--cleanup=verbatim");
 
        if ((flags & ALLOW_EMPTY))
@@ -1012,6 +1060,16 @@ static int rest_is_empty(const struct strbuf *sb, int start)
        return 1;
 }
 
+void cleanup_message(struct strbuf *msgbuf,
+       enum commit_msg_cleanup_mode cleanup_mode, int verbose)
+{
+       if (verbose || /* Truncate the message just before the diff, if any. */
+           cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+               strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));
+       if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
+               strbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+}
+
 /*
  * Find out if the message in the strbuf contains only whitespace and
  * Signed-off-by lines.
@@ -1382,8 +1440,13 @@ static int try_to_commit(struct repository *r,
                msg = &commit_msg;
        }
 
-       cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
-                                         opts->default_msg_cleanup;
+       if (flags & CLEANUP_MSG)
+               cleanup = COMMIT_MSG_CLEANUP_ALL;
+       else if ((opts->signoff || opts->record_origin) &&
+                !opts->explicit_cleanup)
+               cleanup = COMMIT_MSG_CLEANUP_SPACE;
+       else
+               cleanup = opts->default_msg_cleanup;
 
        if (cleanup != COMMIT_MSG_CLEANUP_NONE)
                strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
@@ -2105,6 +2168,41 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
        return !item->commit;
 }
 
+int sequencer_get_last_command(struct repository *r, enum replay_action *action)
+{
+       struct todo_item item;
+       char *eol;
+       const char *todo_file;
+       struct strbuf buf = STRBUF_INIT;
+       int ret = -1;
+
+       todo_file = git_path_todo_file();
+       if (strbuf_read_file(&buf, todo_file, 0) < 0) {
+               if (errno == ENOENT)
+                       return -1;
+               else
+                       return error_errno("unable to open '%s'", todo_file);
+       }
+       eol = strchrnul(buf.buf, '\n');
+       if (buf.buf != eol && eol[-1] == '\r')
+               eol--; /* strip Carriage Return */
+       if (parse_insn_line(r, &item, buf.buf, buf.buf, eol))
+               goto fail;
+       if (item.command == TODO_PICK)
+               *action = REPLAY_PICK;
+       else if (item.command == TODO_REVERT)
+               *action = REPLAY_REVERT;
+       else
+               goto fail;
+
+       ret = 0;
+
+ fail:
+       strbuf_release(&buf);
+
+       return ret;
+}
+
 int todo_list_parse_insn_buffer(struct repository *r, char *buf,
                                struct todo_list *todo_list)
 {
@@ -2188,6 +2286,57 @@ static ssize_t strbuf_read_file_or_whine(struct strbuf *sb, const char *path)
        return len;
 }
 
+static int have_finished_the_last_pick(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+       const char *eol;
+       const char *todo_path = git_path_todo_file();
+       int ret = 0;
+
+       if (strbuf_read_file(&buf, todo_path, 0) < 0) {
+               if (errno == ENOENT) {
+                       return 0;
+               } else {
+                       error_errno("unable to open '%s'", todo_path);
+                       return 0;
+               }
+       }
+       /* If there is only one line then we are done */
+       eol = strchr(buf.buf, '\n');
+       if (!eol || !eol[1])
+               ret = 1;
+
+       strbuf_release(&buf);
+
+       return ret;
+}
+
+void sequencer_post_commit_cleanup(struct repository *r)
+{
+       struct replay_opts opts = REPLAY_OPTS_INIT;
+       int need_cleanup = 0;
+
+       if (file_exists(git_path_cherry_pick_head(r))) {
+               unlink(git_path_cherry_pick_head(r));
+               opts.action = REPLAY_PICK;
+               need_cleanup = 1;
+       }
+
+       if (file_exists(git_path_revert_head(r))) {
+               unlink(git_path_revert_head(r));
+               opts.action = REPLAY_REVERT;
+               need_cleanup = 1;
+       }
+
+       if (!need_cleanup)
+               return;
+
+       if (!have_finished_the_last_pick())
+               return;
+
+       sequencer_remove_state(&opts);
+}
+
 static int read_populate_todo(struct repository *r,
                              struct todo_list *todo_list,
                              struct replay_opts *opts)
@@ -2303,7 +2452,10 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
                opts->allow_rerere_auto =
                        git_config_bool_or_int(key, value, &error_flag) ?
                                RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
-       else
+       else if (!strcmp(key, "options.default-msg-cleanup")) {
+               opts->explicit_cleanup = 1;
+               opts->default_msg_cleanup = get_cleanup_mode(value, 1);
+       } else
                return error(_("invalid key: %s"), key);
 
        if (!error_flag)
@@ -2428,14 +2580,15 @@ static void write_strategy_opts(struct replay_opts *opts)
 }
 
 int write_basic_state(struct replay_opts *opts, const char *head_name,
-                     const char *onto, const char *orig_head)
+                     struct commit *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);
+               write_file(rebase_path_onto(), "%s\n",
+                          oid_to_hex(&onto->object.oid));
        if (orig_head)
                write_file(rebase_path_orig_head(), "%s\n", orig_head);
 
@@ -2725,6 +2878,11 @@ static int save_opts(struct replay_opts *opts)
                                "options.allow-rerere-auto",
                                opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
                                "true" : "false");
+
+       if (opts->explicit_cleanup)
+               res |= git_config_set_in_file_gently(opts_file,
+                               "options.default-msg-cleanup",
+                               describe_cleanup_mode(opts->default_msg_cleanup));
        return res;
 }
 
@@ -3446,10 +3604,11 @@ 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)
+static int run_git_checkout(struct repository *r, struct replay_opts *opts,
+                           const char *commit, const char *action)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
+       int ret;
 
        cmd.git_cmd = 1;
 
@@ -3458,26 +3617,32 @@ static int run_git_checkout(struct replay_opts *opts, const char *commit,
        argv_array_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
 
        if (opts->verbose)
-               return run_command(&cmd);
+               ret = run_command(&cmd);
        else
-               return run_command_silent_on_success(&cmd);
+               ret = run_command_silent_on_success(&cmd);
+
+       if (!ret)
+               discard_index(r->index);
+
+       return ret;
 }
 
-int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit)
+int prepare_branch_to_be_rebased(struct repository *r, 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))
+               if (run_git_checkout(r, 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,
+static int checkout_onto(struct repository *r, struct replay_opts *opts,
+                        const char *onto_name, const struct object_id *onto,
                         const char *orig_head)
 {
        struct object_id oid;
@@ -3486,7 +3651,7 @@ static int checkout_onto(struct replay_opts *opts,
        if (get_oid(orig_head, &oid))
                return error(_("%s: not a valid OID"), orig_head);
 
-       if (run_git_checkout(opts, onto, action)) {
+       if (run_git_checkout(r, opts, oid_to_hex(onto), action)) {
                apply_autostash(opts);
                sequencer_remove_state(opts);
                return error(_("could not detach HEAD"));
@@ -4762,16 +4927,16 @@ static int skip_unnecessary_picks(struct repository *r,
 
 int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
                    const char *shortrevisions, const char *onto_name,
-                   const char *onto, const char *orig_head, struct string_list *commands,
-                   unsigned autosquash, struct todo_list *todo_list)
+                   struct commit *onto, const char *orig_head,
+                   struct string_list *commands, unsigned autosquash,
+                   struct todo_list *todo_list)
 {
        const char *shortonto, *todo_file = rebase_path_todo();
        struct todo_list new_todo = TODO_LIST_INIT;
        struct strbuf *buf = &todo_list->buf;
-       struct object_id oid;
+       struct object_id oid = onto->object.oid;
        int res;
 
-       get_oid(onto, &oid);
        shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
 
        if (buf->len == 0) {
@@ -4814,7 +4979,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
        if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||
            todo_list_check(todo_list, &new_todo)) {
                fprintf(stderr, _(edit_todo_list_advice));
-               checkout_onto(opts, onto_name, onto, orig_head);
+               checkout_onto(r, opts, onto_name, &onto->object.oid, orig_head);
                todo_list_release(&new_todo);
 
                return -1;
@@ -4833,7 +4998,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
 
        todo_list_release(&new_todo);
 
-       if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
+       if (checkout_onto(r, opts, onto_name, &oid, orig_head))
                return -1;
 
        if (require_clean_work_tree(r, "rebase", "", 1, 1))
index a515ee44578e6d8a568544749278c67feb4d233a..0c494b83d43e2c0d71822a23dd274176fe743490 100644 (file)
@@ -48,6 +48,7 @@ struct replay_opts {
 
        char *gpg_sign;
        enum commit_msg_cleanup_mode default_msg_cleanup;
+       int explicit_cleanup;
 
        /* Merge strategy */
        char *strategy;
@@ -150,7 +151,7 @@ void todo_list_add_exec_commands(struct todo_list *todo_list,
 int check_todo_list_from_file(struct repository *r);
 int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
                    const char *shortrevisions, const char *onto_name,
-                   const char *onto, const char *orig_head, struct string_list *commands,
+                   struct commit *onto, const char *orig_head, struct string_list *commands,
                    unsigned autosquash, struct todo_list *todo_list);
 int todo_list_rearrange_squash(struct todo_list *todo_list);
 
@@ -162,7 +163,14 @@ int todo_list_rearrange_squash(struct todo_list *todo_list);
  */
 void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
 
-void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);
+void append_conflicts_hint(struct index_state *istate,
+               struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);
+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
+       int use_editor);
+
+void cleanup_message(struct strbuf *msgbuf,
+       enum commit_msg_cleanup_mode cleanup_mode, int verbose);
+
 int message_is_empty(const struct strbuf *sb,
                     enum commit_msg_cleanup_mode cleanup_mode);
 int template_untouched(const struct strbuf *sb, const char *template_file,
@@ -175,7 +183,8 @@ void commit_post_rewrite(struct repository *r,
                         const struct commit *current_head,
                         const struct object_id *new_head);
 
-int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit);
+int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
+                                const char *commit);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
@@ -190,4 +199,7 @@ int read_author_script(const char *path, char **name, char **email, char **date,
 
 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);
+                     struct commit *onto, const char *orig_head);
+void sequencer_post_commit_cleanup(struct repository *r);
+int sequencer_get_last_command(struct repository* r,
+                              enum replay_action *action);
diff --git a/serve.h b/serve.h
index fe65ba9f469018c05dc1c930381cf49eb30357c1..42ddca7f8b4fdd6bb78ab6da710a086645dd44f2 100644 (file)
--- a/serve.h
+++ b/serve.h
@@ -2,14 +2,14 @@
 #define SERVE_H
 
 struct argv_array;
-extern int has_capability(const struct argv_array *keys, const char *capability,
-                         const char **value);
+int has_capability(const struct argv_array *keys, const char *capability,
+                  const char **value);
 
 struct serve_options {
        unsigned advertise_capabilities;
        unsigned stateless_rpc;
 };
 #define SERVE_OPTIONS_INIT { 0 }
-extern void serve(struct serve_options *options);
+void serve(struct serve_options *options);
 
 #endif /* SERVE_H */
diff --git a/setup.c b/setup.c
index d0c958c3b2a4a7d4b53970b0e623ed8f6e6a4be4..8dcb4631f7d330a290ab0bb6810f24999f65a1b7 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -164,7 +164,8 @@ int check_filename(const char *prefix, const char *arg)
        die_errno(_("failed to stat '%s'"), arg);
 }
 
-static void NORETURN die_verify_filename(const char *prefix,
+static void NORETURN die_verify_filename(struct repository *r,
+                                        const char *prefix,
                                         const char *arg,
                                         int diagnose_misspelt_rev)
 {
@@ -179,7 +180,7 @@ static void NORETURN die_verify_filename(const char *prefix,
         * let maybe_die_on_misspelt_object_name() even trigger.
         */
        if (!(arg[0] == ':' && !isalnum(arg[1])))
-               maybe_die_on_misspelt_object_name(arg, prefix);
+               maybe_die_on_misspelt_object_name(r, arg, prefix);
 
        /* ... or fall back the most general message. */
        die(_("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
@@ -234,7 +235,7 @@ void verify_filename(const char *prefix,
                die(_("option '%s' must come before non-option arguments"), arg);
        if (looks_like_pathspec(arg) || check_filename(prefix, arg))
                return;
-       die_verify_filename(prefix, arg, diagnose_misspelt_rev);
+       die_verify_filename(the_repository, prefix, arg, diagnose_misspelt_rev);
 }
 
 /*
index 7678b23b36c291c7b1b6656e0c42fc14ea12da15..5afcd011c6be3c7b9add226b098860b88c77c2fd 100644 (file)
@@ -3,10 +3,10 @@
 
 typedef const unsigned char *sha1_access_fn(size_t index, void *table);
 
-extern int sha1_pos(const unsigned char *sha1,
-                   void *table,
-                   size_t nr,
-                   sha1_access_fn fn);
+int sha1_pos(const unsigned char *sha1,
+            void *table,
+            size_t nr,
+            sha1_access_fn fn);
 
 /*
  * Searches for sha1 in table, using the given fanout table to determine the
index 07c71a7567012aedc7faeccfa5c89cf2363d42c0..775a73d8adf6748cd67796a6b13ea1c4843d9f6a 100644 (file)
 #include "packfile.h"
 #include "object-store.h"
 #include "repository.h"
+#include "submodule.h"
 #include "midx.h"
 #include "commit-reach.h"
 
-static int get_oid_oneline(const char *, struct object_id *, struct commit_list *);
+static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *);
 
-typedef int (*disambiguate_hint_fn)(const struct object_id *, void *);
+typedef int (*disambiguate_hint_fn)(struct repository *, const struct object_id *, void *);
 
 struct disambiguate_state {
        int len; /* length of prefix in hex chars */
        char hex_pfx[GIT_MAX_HEXSZ + 1];
        struct object_id bin_pfx;
 
+       struct repository *repo;
        disambiguate_hint_fn fn;
        void *cb_data;
        struct object_id candidate;
@@ -38,7 +40,7 @@ struct disambiguate_state {
 static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
 {
        if (ds->always_call_fn) {
-               ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
+               ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0;
                return;
        }
        if (!ds->candidate_exists) {
@@ -58,7 +60,7 @@ static void update_candidates(struct disambiguate_state *ds, const struct object
        }
 
        if (!ds->candidate_checked) {
-               ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data);
+               ds->candidate_ok = ds->fn(ds->repo, &ds->candidate, ds->cb_data);
                ds->disambiguate_fn_used = 1;
                ds->candidate_checked = 1;
        }
@@ -71,7 +73,7 @@ static void update_candidates(struct disambiguate_state *ds, const struct object
        }
 
        /* if we reach this point, we know ds->candidate satisfies fn */
-       if (ds->fn(current, ds->cb_data)) {
+       if (ds->fn(ds->repo, current, ds->cb_data)) {
                /*
                 * if both current and candidate satisfy fn, we cannot
                 * disambiguate.
@@ -89,9 +91,7 @@ static void find_short_object_filename(struct disambiguate_state *ds)
 {
        struct object_directory *odb;
 
-       for (odb = the_repository->objects->odb;
-            odb && !ds->ambiguous;
-            odb = odb->next) {
+       for (odb = ds->repo->objects->odb; odb && !ds->ambiguous; odb = odb->next) {
                int pos;
                struct oid_array *loose_objects;
 
@@ -182,10 +182,10 @@ static void find_short_packed_object(struct disambiguate_state *ds)
        struct multi_pack_index *m;
        struct packed_git *p;
 
-       for (m = get_multi_pack_index(the_repository); m && !ds->ambiguous;
+       for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous;
             m = m->next)
                unique_in_midx(m, ds);
-       for (p = get_packed_git(the_repository); p && !ds->ambiguous;
+       for (p = get_packed_git(ds->repo); p && !ds->ambiguous;
             p = p->next)
                unique_in_pack(p, ds);
 }
@@ -215,7 +215,7 @@ static int finish_object_disambiguation(struct disambiguate_state *ds,
                 * same repository!
                 */
                ds->candidate_ok = (!ds->disambiguate_fn_used ||
-                                   ds->fn(&ds->candidate, ds->cb_data));
+                                   ds->fn(ds->repo, &ds->candidate, ds->cb_data));
 
        if (!ds->candidate_ok)
                return SHORT_NAME_AMBIGUOUS;
@@ -224,59 +224,67 @@ static int finish_object_disambiguation(struct disambiguate_state *ds,
        return 0;
 }
 
-static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused)
+static int disambiguate_commit_only(struct repository *r,
+                                   const struct object_id *oid,
+                                   void *cb_data_unused)
 {
-       int kind = oid_object_info(the_repository, oid, NULL);
+       int kind = oid_object_info(r, oid, NULL);
        return kind == OBJ_COMMIT;
 }
 
-static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused)
+static int disambiguate_committish_only(struct repository *r,
+                                       const struct object_id *oid,
+                                       void *cb_data_unused)
 {
        struct object *obj;
        int kind;
 
-       kind = oid_object_info(the_repository, oid, NULL);
+       kind = oid_object_info(r, oid, NULL);
        if (kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
                return 0;
 
        /* We need to do this the hard way... */
-       obj = deref_tag(the_repository, parse_object(the_repository, oid),
-                       NULL, 0);
+       obj = deref_tag(r, parse_object(r, oid), NULL, 0);
        if (obj && obj->type == OBJ_COMMIT)
                return 1;
        return 0;
 }
 
-static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused)
+static int disambiguate_tree_only(struct repository *r,
+                                 const struct object_id *oid,
+                                 void *cb_data_unused)
 {
-       int kind = oid_object_info(the_repository, oid, NULL);
+       int kind = oid_object_info(r, oid, NULL);
        return kind == OBJ_TREE;
 }
 
-static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused)
+static int disambiguate_treeish_only(struct repository *r,
+                                    const struct object_id *oid,
+                                    void *cb_data_unused)
 {
        struct object *obj;
        int kind;
 
-       kind = oid_object_info(the_repository, oid, NULL);
+       kind = oid_object_info(r, oid, NULL);
        if (kind == OBJ_TREE || kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
                return 0;
 
        /* We need to do this the hard way... */
-       obj = deref_tag(the_repository, parse_object(the_repository, oid),
-                       NULL, 0);
+       obj = deref_tag(r, parse_object(r, oid), NULL, 0);
        if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
                return 1;
        return 0;
 }
 
-static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused)
+static int disambiguate_blob_only(struct repository *r,
+                                 const struct object_id *oid,
+                                 void *cb_data_unused)
 {
-       int kind = oid_object_info(the_repository, oid, NULL);
+       int kind = oid_object_info(r, oid, NULL);
        return kind == OBJ_BLOB;
 }
 
@@ -310,7 +318,8 @@ int set_disambiguate_hint_config(const char *var, const char *value)
        return error("unknown hint type for '%s': %s", var, value);
 }
 
-static int init_object_disambiguation(const char *name, int len,
+static int init_object_disambiguation(struct repository *r,
+                                     const char *name, int len,
                                      struct disambiguate_state *ds)
 {
        int i;
@@ -341,7 +350,8 @@ static int init_object_disambiguation(const char *name, int len,
 
        ds->len = len;
        ds->hex_pfx[len] = '\0';
-       prepare_alt_odb(the_repository);
+       ds->repo = r;
+       prepare_alt_odb(r);
        return 0;
 }
 
@@ -351,25 +361,25 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)
        struct strbuf desc = STRBUF_INIT;
        int type;
 
-       if (ds->fn && !ds->fn(oid, ds->cb_data))
+       if (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))
                return 0;
 
-       type = oid_object_info(the_repository, oid, NULL);
+       type = oid_object_info(ds->repo, oid, NULL);
        if (type == OBJ_COMMIT) {
-               struct commit *commit = lookup_commit(the_repository, oid);
+               struct commit *commit = lookup_commit(ds->repo, oid);
                if (commit) {
                        struct pretty_print_context pp = {0};
                        pp.date_mode.type = DATE_SHORT;
                        format_commit_message(commit, " %ad - %s", &desc, &pp);
                }
        } else if (type == OBJ_TAG) {
-               struct tag *tag = lookup_tag(the_repository, oid);
+               struct tag *tag = lookup_tag(ds->repo, oid);
                if (!parse_tag(tag) && tag->tag)
                        strbuf_addf(&desc, " %s", tag->tag);
        }
 
        advise("  %s %s%s",
-              find_unique_abbrev(oid, DEFAULT_ABBREV),
+              repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),
               type_name(type) ? type_name(type) : "unknown type",
               desc.buf);
 
@@ -383,10 +393,18 @@ static int collect_ambiguous(const struct object_id *oid, void *data)
        return 0;
 }
 
+static int repo_collect_ambiguous(struct repository *r,
+                                 const struct object_id *oid,
+                                 void *data)
+{
+       return collect_ambiguous(oid, data);
+}
+
+static struct repository *sort_ambiguous_repo;
 static int sort_ambiguous(const void *a, const void *b)
 {
-       int a_type = oid_object_info(the_repository, a, NULL);
-       int b_type = oid_object_info(the_repository, b, NULL);
+       int a_type = oid_object_info(sort_ambiguous_repo, a, NULL);
+       int b_type = oid_object_info(sort_ambiguous_repo, b, NULL);
        int a_type_sort;
        int b_type_sort;
 
@@ -411,7 +429,16 @@ static int sort_ambiguous(const void *a, const void *b)
        return a_type_sort > b_type_sort ? 1 : -1;
 }
 
-static enum get_oid_result get_short_oid(const char *name, int len,
+static void sort_ambiguous_oid_array(struct repository *r, struct oid_array *a)
+{
+       /* mutex will be needed if this code is to be made thread safe */
+       sort_ambiguous_repo = r;
+       QSORT(a->oid, a->nr, sort_ambiguous);
+       sort_ambiguous_repo = NULL;
+}
+
+static enum get_oid_result get_short_oid(struct repository *r,
+                                        const char *name, int len,
                                         struct object_id *oid,
                                         unsigned flags)
 {
@@ -419,7 +446,7 @@ static enum get_oid_result get_short_oid(const char *name, int len,
        struct disambiguate_state ds;
        int quietly = !!(flags & GET_OID_QUIETLY);
 
-       if (init_object_disambiguation(name, len, &ds) < 0)
+       if (init_object_disambiguation(r, name, len, &ds) < 0)
                return -1;
 
        if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS))
@@ -469,8 +496,8 @@ static enum get_oid_result get_short_oid(const char *name, int len,
                        ds.fn = NULL;
 
                advise(_("The candidates are:"));
-               for_each_abbrev(ds.hex_pfx, collect_ambiguous, &collect);
-               QSORT(collect.oid, collect.nr, sort_ambiguous);
+               repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
+               sort_ambiguous_oid_array(r, &collect);
 
                if (oid_array_for_each(&collect, show_ambiguous_object, &ds))
                        BUG("show_ambiguous_object shouldn't return non-zero");
@@ -480,17 +507,18 @@ static enum get_oid_result get_short_oid(const char *name, int len,
        return status;
 }
 
-int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
+int repo_for_each_abbrev(struct repository *r, const char *prefix,
+                        each_abbrev_fn fn, void *cb_data)
 {
        struct oid_array collect = OID_ARRAY_INIT;
        struct disambiguate_state ds;
        int ret;
 
-       if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0)
+       if (init_object_disambiguation(r, prefix, strlen(prefix), &ds) < 0)
                return -1;
 
        ds.always_call_fn = 1;
-       ds.fn = collect_ambiguous;
+       ds.fn = repo_collect_ambiguous;
        ds.cb_data = &collect;
        find_short_object_filename(&ds);
        find_short_packed_object(&ds);
@@ -517,6 +545,7 @@ struct min_abbrev_data {
        unsigned int init_len;
        unsigned int cur_len;
        char *hex;
+       struct repository *repo;
        const struct object_id *oid;
 };
 
@@ -545,6 +574,13 @@ static int extend_abbrev_len(const struct object_id *oid, void *cb_data)
        return 0;
 }
 
+static int repo_extend_abbrev_len(struct repository *r,
+                                 const struct object_id *oid,
+                                 void *cb_data)
+{
+       return extend_abbrev_len(oid, cb_data);
+}
+
 static void find_abbrev_len_for_midx(struct multi_pack_index *m,
                                     struct min_abbrev_data *mad)
 {
@@ -622,21 +658,22 @@ static void find_abbrev_len_packed(struct min_abbrev_data *mad)
        struct multi_pack_index *m;
        struct packed_git *p;
 
-       for (m = get_multi_pack_index(the_repository); m; m = m->next)
+       for (m = get_multi_pack_index(mad->repo); m; m = m->next)
                find_abbrev_len_for_midx(m, mad);
-       for (p = get_packed_git(the_repository); p; p = p->next)
+       for (p = get_packed_git(mad->repo); p; p = p->next)
                find_abbrev_len_for_pack(p, mad);
 }
 
-int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len)
+int repo_find_unique_abbrev_r(struct repository *r, char *hex,
+                             const struct object_id *oid, int len)
 {
        struct disambiguate_state ds;
        struct min_abbrev_data mad;
        struct object_id oid_ret;
-       const unsigned hexsz = the_hash_algo->hexsz;
+       const unsigned hexsz = r->hash_algo->hexsz;
 
        if (len < 0) {
-               unsigned long count = approximate_object_count();
+               unsigned long count = repo_approximate_object_count(r);
                /*
                 * Add one because the MSB only tells us the highest bit set,
                 * not including the value of all the _other_ bits (so "15"
@@ -661,6 +698,7 @@ int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len)
        if (len == hexsz || !len)
                return hexsz;
 
+       mad.repo = r;
        mad.init_len = len;
        mad.cur_len = len;
        mad.hex = hex;
@@ -668,10 +706,10 @@ int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len)
 
        find_abbrev_len_packed(&mad);
 
-       if (init_object_disambiguation(hex, mad.cur_len, &ds) < 0)
+       if (init_object_disambiguation(r, hex, mad.cur_len, &ds) < 0)
                return -1;
 
-       ds.fn = extend_abbrev_len;
+       ds.fn = repo_extend_abbrev_len;
        ds.always_call_fn = 1;
        ds.cb_data = (void *)&mad;
 
@@ -682,13 +720,15 @@ int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len)
        return mad.cur_len;
 }
 
-const char *find_unique_abbrev(const struct object_id *oid, int len)
+const char *repo_find_unique_abbrev(struct repository *r,
+                                   const struct object_id *oid,
+                                   int len)
 {
        static int bufno;
        static char hexbuffer[4][GIT_MAX_HEXSZ + 1];
        char *hex = hexbuffer[bufno];
        bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
-       find_unique_abbrev_r(hex, oid, len);
+       repo_find_unique_abbrev_r(r, hex, oid, len);
        return hex;
 }
 
@@ -743,11 +783,11 @@ static inline int push_mark(const char *string, int len)
        return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
 }
 
-static enum get_oid_result get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags);
-static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
+static enum get_oid_result get_oid_1(struct repository *r, const char *name, int len, struct object_id *oid, unsigned lookup_flags);
+static int interpret_nth_prior_checkout(struct repository *r, const char *name, int namelen, struct strbuf *buf);
 
-static int get_oid_basic(const char *str, int len, struct object_id *oid,
-                         unsigned int flags)
+static int get_oid_basic(struct repository *r, const char *str, int len,
+                        struct object_id *oid, unsigned int flags)
 {
        static const char *warn_msg = "refname '%.*s' is ambiguous.";
        static const char *object_name_msg = N_(
@@ -765,9 +805,9 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
        int refs_found = 0;
        int at, reflog_len, nth_prior = 0;
 
-       if (len == the_hash_algo->hexsz && !get_oid_hex(str, oid)) {
+       if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
                if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
-                       refs_found = dwim_ref(str, len, &tmp_oid, &real_ref);
+                       refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref);
                        if (refs_found > 0) {
                                warning(warn_msg, len, str);
                                if (advice_object_name_warning)
@@ -808,8 +848,8 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
                struct strbuf buf = STRBUF_INIT;
                int detached;
 
-               if (interpret_nth_prior_checkout(str, len, &buf) > 0) {
-                       detached = (buf.len == the_hash_algo->hexsz && !get_oid_hex(buf.buf, oid));
+               if (interpret_nth_prior_checkout(r, str, len, &buf) > 0) {
+                       detached = (buf.len == r->hash_algo->hexsz && !get_oid_hex(buf.buf, oid));
                        strbuf_release(&buf);
                        if (detached)
                                return 0;
@@ -818,18 +858,18 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 
        if (!len && reflog_len)
                /* allow "@{...}" to mean the current branch reflog */
-               refs_found = dwim_ref("HEAD", 4, oid, &real_ref);
+               refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref);
        else if (reflog_len)
-               refs_found = dwim_log(str, len, oid, &real_ref);
+               refs_found = repo_dwim_log(r, str, len, oid, &real_ref);
        else
-               refs_found = dwim_ref(str, len, oid, &real_ref);
+               refs_found = repo_dwim_ref(r, str, len, oid, &real_ref);
 
        if (!refs_found)
                return -1;
 
        if (warn_ambiguous_refs && !(flags & GET_OID_QUIETLY) &&
            (refs_found > 1 ||
-            !get_short_oid(str, len, &tmp_oid, GET_OID_QUIETLY)))
+            !get_short_oid(r, str, len, &tmp_oid, GET_OID_QUIETLY)))
                warning(warn_msg, len, str);
 
        if (reflog_len) {
@@ -861,7 +901,8 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
                                return -1;
                        }
                }
-               if (read_ref_at(real_ref, flags, at_time, nth, oid, NULL,
+               if (read_ref_at(get_main_ref_store(r),
+                               real_ref, flags, at_time, nth, oid, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (!len) {
                                if (starts_with(real_ref, "refs/heads/")) {
@@ -893,18 +934,19 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
        return 0;
 }
 
-static enum get_oid_result get_parent(const char *name, int len,
+static enum get_oid_result get_parent(struct repository *r,
+                                     const char *name, int len,
                                      struct object_id *result, int idx)
 {
        struct object_id oid;
-       enum get_oid_result ret = get_oid_1(name, len, &oid,
+       enum get_oid_result ret = get_oid_1(r, name, len, &oid,
                                            GET_OID_COMMITTISH);
        struct commit *commit;
        struct commit_list *p;
 
        if (ret)
                return ret;
-       commit = lookup_commit_reference(the_repository, &oid);
+       commit = lookup_commit_reference(r, &oid);
        if (parse_commit(commit))
                return MISSING_OBJECT;
        if (!idx) {
@@ -922,7 +964,8 @@ static enum get_oid_result get_parent(const char *name, int len,
        return MISSING_OBJECT;
 }
 
-static enum get_oid_result get_nth_ancestor(const char *name, int len,
+static enum get_oid_result get_nth_ancestor(struct repository *r,
+                                           const char *name, int len,
                                            struct object_id *result,
                                            int generation)
 {
@@ -930,10 +973,10 @@ static enum get_oid_result get_nth_ancestor(const char *name, int len,
        struct commit *commit;
        int ret;
 
-       ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH);
+       ret = get_oid_1(r, name, len, &oid, GET_OID_COMMITTISH);
        if (ret)
                return ret;
-       commit = lookup_commit_reference(the_repository, &oid);
+       commit = lookup_commit_reference(r, &oid);
        if (!commit)
                return MISSING_OBJECT;
 
@@ -946,20 +989,20 @@ static enum get_oid_result get_nth_ancestor(const char *name, int len,
        return FOUND;
 }
 
-struct object *peel_to_type(const char *name, int namelen,
-                           struct object *o, enum object_type expected_type)
+struct object *repo_peel_to_type(struct repository *r, const char *name, int namelen,
+                                struct object *o, enum object_type expected_type)
 {
        if (name && !namelen)
                namelen = strlen(name);
        while (1) {
-               if (!o || (!o->parsed && !parse_object(the_repository, &o->oid)))
+               if (!o || (!o->parsed && !parse_object(r, &o->oid)))
                        return NULL;
                if (expected_type == OBJ_ANY || o->type == expected_type)
                        return o;
                if (o->type == OBJ_TAG)
                        o = ((struct tag*) o)->tagged;
                else if (o->type == OBJ_COMMIT)
-                       o = &(get_commit_tree(((struct commit *)o))->object);
+                       o = &(repo_get_commit_tree(r, ((struct commit *)o))->object);
                else {
                        if (name)
                                error("%.*s: expected %s type, but the object "
@@ -971,8 +1014,8 @@ struct object *peel_to_type(const char *name, int namelen,
        }
 }
 
-static int peel_onion(const char *name, int len, struct object_id *oid,
-                     unsigned lookup_flags)
+static int peel_onion(struct repository *r, const char *name, int len,
+                     struct object_id *oid, unsigned lookup_flags)
 {
        struct object_id outer;
        const char *sp;
@@ -1022,15 +1065,15 @@ static int peel_onion(const char *name, int len, struct object_id *oid,
        else if (expected_type == OBJ_TREE)
                lookup_flags |= GET_OID_TREEISH;
 
-       if (get_oid_1(name, sp - name - 2, &outer, lookup_flags))
+       if (get_oid_1(r, name, sp - name - 2, &outer, lookup_flags))
                return -1;
 
-       o = parse_object(the_repository, &outer);
+       o = parse_object(r, &outer);
        if (!o)
                return -1;
        if (!expected_type) {
-               o = deref_tag(the_repository, o, name, sp - name - 2);
-               if (!o || (!o->parsed && !parse_object(the_repository, &o->oid)))
+               o = deref_tag(r, o, name, sp - name - 2);
+               if (!o || (!o->parsed && !parse_object(r, &o->oid)))
                        return -1;
                oidcpy(oid, &o->oid);
                return 0;
@@ -1041,7 +1084,7 @@ static int peel_onion(const char *name, int len, struct object_id *oid,
         * if we do not get the needed object, we should
         * barf.
         */
-       o = peel_to_type(name, len, o, expected_type);
+       o = repo_peel_to_type(r, name, len, o, expected_type);
        if (!o)
                return -1;
 
@@ -1061,14 +1104,16 @@ static int peel_onion(const char *name, int len, struct object_id *oid,
 
                prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
                commit_list_insert((struct commit *)o, &list);
-               ret = get_oid_oneline(prefix, oid, list);
+               ret = get_oid_oneline(r, prefix, oid, list);
                free(prefix);
                return ret;
        }
        return 0;
 }
 
-static int get_describe_name(const char *name, int len, struct object_id *oid)
+static int get_describe_name(struct repository *r,
+                            const char *name, int len,
+                            struct object_id *oid)
 {
        const char *cp;
        unsigned flags = GET_OID_QUIETLY | GET_OID_COMMIT;
@@ -1082,14 +1127,16 @@ static int get_describe_name(const char *name, int len, struct object_id *oid)
                        if (ch == 'g' && cp[-1] == '-') {
                                cp++;
                                len -= cp - name;
-                               return get_short_oid(cp, len, oid, flags);
+                               return get_short_oid(r,
+                                                    cp, len, oid, flags);
                        }
                }
        }
        return -1;
 }
 
-static enum get_oid_result get_oid_1(const char *name, int len,
+static enum get_oid_result get_oid_1(struct repository *r,
+                                    const char *name, int len,
                                     struct object_id *oid,
                                     unsigned lookup_flags)
 {
@@ -1118,25 +1165,25 @@ static enum get_oid_result get_oid_1(const char *name, int len,
                if (!num && len1 == len - 1)
                        num = 1;
                if (has_suffix == '^')
-                       return get_parent(name, len1, oid, num);
+                       return get_parent(r, name, len1, oid, num);
                /* else if (has_suffix == '~') -- goes without saying */
-               return get_nth_ancestor(name, len1, oid, num);
+               return get_nth_ancestor(r, name, len1, oid, num);
        }
 
-       ret = peel_onion(name, len, oid, lookup_flags);
+       ret = peel_onion(r, name, len, oid, lookup_flags);
        if (!ret)
                return FOUND;
 
-       ret = get_oid_basic(name, len, oid, lookup_flags);
+       ret = get_oid_basic(r, name, len, oid, lookup_flags);
        if (!ret)
                return FOUND;
 
        /* It could be describe output that is "SOMETHING-gXXXX" */
-       ret = get_describe_name(name, len, oid);
+       ret = get_describe_name(r, name, len, oid);
        if (!ret)
                return FOUND;
 
-       return get_short_oid(name, len, oid, lookup_flags);
+       return get_short_oid(r, name, len, oid, lookup_flags);
 }
 
 /*
@@ -1155,15 +1202,21 @@ static enum get_oid_result get_oid_1(const char *name, int len,
 /* Remember to update object flag allocation in object.h */
 #define ONELINE_SEEN (1u<<20)
 
+struct handle_one_ref_cb {
+       struct repository *repo;
+       struct commit_list **list;
+};
+
 static int handle_one_ref(const char *path, const struct object_id *oid,
                          int flag, void *cb_data)
 {
-       struct commit_list **list = cb_data;
-       struct object *object = parse_object(the_repository, oid);
+       struct handle_one_ref_cb *cb = cb_data;
+       struct commit_list **list = cb->list;
+       struct object *object = parse_object(cb->repo, oid);
        if (!object)
                return 0;
        if (object->type == OBJ_TAG) {
-               object = deref_tag(the_repository, object, path,
+               object = deref_tag(cb->repo, object, path,
                                   strlen(path));
                if (!object)
                        return 0;
@@ -1174,8 +1227,9 @@ static int handle_one_ref(const char *path, const struct object_id *oid,
        return 0;
 }
 
-static int get_oid_oneline(const char *prefix, struct object_id *oid,
-                           struct commit_list *list)
+static int get_oid_oneline(struct repository *r,
+                          const char *prefix, struct object_id *oid,
+                          struct commit_list *list)
 {
        struct commit_list *backup = NULL, *l;
        int found = 0;
@@ -1206,7 +1260,7 @@ static int get_oid_oneline(const char *prefix, struct object_id *oid,
                int matches;
 
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
-               if (!parse_object(the_repository, &commit->object.oid))
+               if (!parse_object(r, &commit->object.oid))
                        continue;
                buf = get_commit_buffer(commit, NULL);
                p = strstr(buf, "\n\n");
@@ -1258,7 +1312,8 @@ static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid
  * Parse @{-N} syntax, return the number of characters parsed
  * if successful; otherwise signal an error with negative value.
  */
-static int interpret_nth_prior_checkout(const char *name, int namelen,
+static int interpret_nth_prior_checkout(struct repository *r,
+                                       const char *name, int namelen,
                                        struct strbuf *buf)
 {
        long nth;
@@ -1282,18 +1337,22 @@ static int interpret_nth_prior_checkout(const char *name, int namelen,
        cb.remaining = nth;
        strbuf_init(&cb.buf, 20);
 
-       retval = 0;
-       if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) {
+       retval = refs_for_each_reflog_ent_reverse(get_main_ref_store(r),
+                       "HEAD", grab_nth_branch_switch, &cb);
+       if (0 < retval) {
                strbuf_reset(buf);
                strbuf_addbuf(buf, &cb.buf);
                retval = brace - name + 1;
-       }
+       } else
+               retval = 0;
 
        strbuf_release(&cb.buf);
        return retval;
 }
 
-int get_oid_mb(const char *name, struct object_id *oid)
+int repo_get_oid_mb(struct repository *r,
+                   const char *name,
+                   struct object_id *oid)
 {
        struct commit *one, *two;
        struct commit_list *mbs;
@@ -1303,27 +1362,29 @@ int get_oid_mb(const char *name, struct object_id *oid)
 
        dots = strstr(name, "...");
        if (!dots)
-               return get_oid(name, oid);
+               return repo_get_oid(r, name, oid);
        if (dots == name)
-               st = get_oid("HEAD", &oid_tmp);
+               st = repo_get_oid(r, "HEAD", &oid_tmp);
        else {
                struct strbuf sb;
                strbuf_init(&sb, dots - name);
                strbuf_add(&sb, name, dots - name);
-               st = get_oid_committish(sb.buf, &oid_tmp);
+               st = repo_get_oid_committish(r, sb.buf, &oid_tmp);
                strbuf_release(&sb);
        }
        if (st)
                return st;
-       one = lookup_commit_reference_gently(the_repository, &oid_tmp, 0);
+       one = lookup_commit_reference_gently(r, &oid_tmp, 0);
        if (!one)
                return -1;
 
-       if (get_oid_committish(dots[3] ? (dots + 3) : "HEAD", &oid_tmp))
+       if (repo_get_oid_committish(r, dots[3] ? (dots + 3) : "HEAD", &oid_tmp))
                return -1;
-       two = lookup_commit_reference_gently(the_repository, &oid_tmp, 0);
+       two = lookup_commit_reference_gently(r, &oid_tmp, 0);
        if (!two)
                return -1;
+       if (r != the_repository)
+               BUG("sorry get_merge_bases() can't take struct repository yet");
        mbs = get_merge_bases(one, two);
        if (!mbs || mbs->next)
                st = -1;
@@ -1357,7 +1418,8 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str
        return 1;
 }
 
-static int reinterpret(const char *name, int namelen, int len,
+static int reinterpret(struct repository *r,
+                      const char *name, int namelen, int len,
                       struct strbuf *buf, unsigned allowed)
 {
        /* we have extra data, which might need further processing */
@@ -1366,7 +1428,7 @@ static int reinterpret(const char *name, int namelen, int len,
        int ret;
 
        strbuf_add(buf, name + len, namelen - len);
-       ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed);
+       ret = repo_interpret_branch_name(r, buf->buf, buf->len, &tmp, allowed);
        /* that data was not interpreted, remove our cruft */
        if (ret < 0) {
                strbuf_setlen(buf, used);
@@ -1379,9 +1441,9 @@ static int reinterpret(const char *name, int namelen, int len,
        return ret - used + len;
 }
 
-static void set_shortened_ref(struct strbuf *buf, const char *ref)
+static void set_shortened_ref(struct repository *r, struct strbuf *buf, const char *ref)
 {
-       char *s = shorten_unambiguous_ref(ref, 0);
+       char *s = refs_shorten_unambiguous_ref(get_main_ref_store(r), ref, 0);
        strbuf_reset(buf);
        strbuf_addstr(buf, s);
        free(s);
@@ -1402,7 +1464,8 @@ static int branch_interpret_allowed(const char *refname, unsigned allowed)
        return 0;
 }
 
-static int interpret_branch_mark(const char *name, int namelen,
+static int interpret_branch_mark(struct repository *r,
+                                const char *name, int namelen,
                                 int at, struct strbuf *buf,
                                 int (*get_mark)(const char *, int),
                                 const char *(*get_data)(struct branch *,
@@ -1435,12 +1498,14 @@ static int interpret_branch_mark(const char *name, int namelen,
        if (!branch_interpret_allowed(value, allowed))
                return -1;
 
-       set_shortened_ref(buf, value);
+       set_shortened_ref(r, buf, value);
        return len + at;
 }
 
-int interpret_branch_name(const char *name, int namelen, struct strbuf *buf,
-                         unsigned allowed)
+int repo_interpret_branch_name(struct repository *r,
+                              const char *name, int namelen,
+                              struct strbuf *buf,
+                              unsigned allowed)
 {
        char *at;
        const char *start;
@@ -1450,14 +1515,14 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf,
                namelen = strlen(name);
 
        if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) {
-               len = interpret_nth_prior_checkout(name, namelen, buf);
+               len = interpret_nth_prior_checkout(r, name, namelen, buf);
                if (!len) {
                        return len; /* syntax Ok, not enough switches */
                } else if (len > 0) {
                        if (len == namelen)
                                return len; /* consumed all */
                        else
-                               return reinterpret(name, namelen, len, buf, allowed);
+                               return reinterpret(r, name, namelen, len, buf, allowed);
                }
        }
 
@@ -1468,17 +1533,17 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf,
                if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) {
                        len = interpret_empty_at(name, namelen, at - name, buf);
                        if (len > 0)
-                               return reinterpret(name, namelen, len, buf,
+                               return reinterpret(r, name, namelen, len, buf,
                                                   allowed);
                }
 
-               len = interpret_branch_mark(name, namelen, at - name, buf,
+               len = interpret_branch_mark(r, name, namelen, at - name, buf,
                                            upstream_mark, branch_get_upstream,
                                            allowed);
                if (len > 0)
                        return len;
 
-               len = interpret_branch_mark(name, namelen, at - name, buf,
+               len = interpret_branch_mark(r, name, namelen, at - name, buf,
                                            push_mark, branch_get_push,
                                            allowed);
                if (len > 0)
@@ -1524,10 +1589,10 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
  * This is like "get_oid_basic()", except it allows "object ID expressions",
  * notably "xyz^" for "parent of xyz"
  */
-int get_oid(const char *name, struct object_id *oid)
+int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
 {
        struct object_context unused;
-       return get_oid_with_context(the_repository, name, 0, oid, &unused);
+       return get_oid_with_context(r, name, 0, oid, &unused);
 }
 
 /*
@@ -1560,43 +1625,48 @@ int get_oidf(struct object_id *oid, const char *fmt, ...)
  * commit-ish. It is merely to give a hint to the disambiguation
  * machinery.
  */
-int get_oid_committish(const char *name, struct object_id *oid)
+int repo_get_oid_committish(struct repository *r,
+                           const char *name,
+                           struct object_id *oid)
 {
        struct object_context unused;
-       return get_oid_with_context(the_repository,
-                                   name, GET_OID_COMMITTISH,
+       return get_oid_with_context(r, name, GET_OID_COMMITTISH,
                                    oid, &unused);
 }
 
-int get_oid_treeish(const char *name, struct object_id *oid)
+int repo_get_oid_treeish(struct repository *r,
+                        const char *name,
+                        struct object_id *oid)
 {
        struct object_context unused;
-       return get_oid_with_context(the_repository,
-                                   name, GET_OID_TREEISH,
+       return get_oid_with_context(r, name, GET_OID_TREEISH,
                                    oid, &unused);
 }
 
-int get_oid_commit(const char *name, struct object_id *oid)
+int repo_get_oid_commit(struct repository *r,
+                       const char *name,
+                       struct object_id *oid)
 {
        struct object_context unused;
-       return get_oid_with_context(the_repository,
-                                   name, GET_OID_COMMIT,
+       return get_oid_with_context(r, name, GET_OID_COMMIT,
                                    oid, &unused);
 }
 
-int get_oid_tree(const char *name, struct object_id *oid)
+int repo_get_oid_tree(struct repository *r,
+                     const char *name,
+                     struct object_id *oid)
 {
        struct object_context unused;
-       return get_oid_with_context(the_repository,
-                                   name, GET_OID_TREE,
+       return get_oid_with_context(r, name, GET_OID_TREE,
                                    oid, &unused);
 }
 
-int get_oid_blob(const char *name, struct object_id *oid)
+int repo_get_oid_blob(struct repository *r,
+                     const char *name,
+                     struct object_id *oid)
 {
        struct object_context unused;
-       return get_oid_with_context(the_repository,
-                                   name, GET_OID_BLOB,
+       return get_oid_with_context(r, name, GET_OID_BLOB,
                                    oid, &unused);
 }
 
@@ -1608,7 +1678,7 @@ static void diagnose_invalid_oid_path(const char *prefix,
                                      int object_name_len)
 {
        struct object_id oid;
-       unsigned mode;
+       unsigned short mode;
 
        if (!prefix)
                prefix = "";
@@ -1635,11 +1705,12 @@ static void diagnose_invalid_oid_path(const char *prefix,
 }
 
 /* Must be called only when :stage:filename doesn't exist. */
-static void diagnose_invalid_index_path(struct index_state *istate,
+static void diagnose_invalid_index_path(struct repository *r,
                                        int stage,
                                        const char *prefix,
                                        const char *filename)
 {
+       struct index_state *istate = r->index;
        const struct cache_entry *ce;
        int pos;
        unsigned namelen = strlen(filename);
@@ -1679,7 +1750,7 @@ static void diagnose_invalid_index_path(struct index_state *istate,
                            ce_stage(ce), filename);
        }
 
-       if (file_exists(filename))
+       if (repo_file_exists(r, filename))
                die("Path '%s' exists on disk, but not in the index.", filename);
        if (is_missing_file_error(errno))
                die("Path '%s' does not exist (neither on disk nor in the index).",
@@ -1689,12 +1760,12 @@ static void diagnose_invalid_index_path(struct index_state *istate,
 }
 
 
-static char *resolve_relative_path(const char *rel)
+static char *resolve_relative_path(struct repository *r, const char *rel)
 {
        if (!starts_with(rel, "./") && !starts_with(rel, "../"))
                return NULL;
 
-       if (!is_inside_work_tree())
+       if (r != the_repository || !is_inside_work_tree())
                die("relative path syntax can't be used outside working tree.");
 
        /* die() inside prefix_path() if resolved path is outside worktree */
@@ -1721,7 +1792,7 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,
        memset(oc, 0, sizeof(*oc));
        oc->mode = S_IFINVALID;
        strbuf_init(&oc->symlink_path, 0);
-       ret = get_oid_1(name, namelen, oid, flags);
+       ret = get_oid_1(repo, name, namelen, oid, flags);
        if (!ret)
                return ret;
        /*
@@ -1737,12 +1808,15 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,
                char *new_path = NULL;
                int pos;
                if (!only_to_die && namelen > 2 && name[1] == '/') {
+                       struct handle_one_ref_cb cb;
                        struct commit_list *list = NULL;
 
-                       for_each_ref(handle_one_ref, &list);
-                       head_ref(handle_one_ref, &list);
+                       cb.repo = repo;
+                       cb.list = &list;
+                       refs_for_each_ref(repo->refs, handle_one_ref, &cb);
+                       refs_head_ref(repo->refs, handle_one_ref, &cb);
                        commit_list_sort_by_date(&list);
-                       return get_oid_oneline(name + 2, oid, list);
+                       return get_oid_oneline(repo, name + 2, oid, list);
                }
                if (namelen < 3 ||
                    name[2] != ':' ||
@@ -1752,7 +1826,7 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,
                        stage = name[1] - '0';
                        cp = name + 3;
                }
-               new_path = resolve_relative_path(cp);
+               new_path = resolve_relative_path(repo, cp);
                if (!new_path) {
                        namelen = namelen - (cp - name);
                } else {
@@ -1764,7 +1838,7 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,
                        oc->path = xstrdup(cp);
 
                if (!repo->index->cache)
-                       repo_read_index(the_repository);
+                       repo_read_index(repo);
                pos = index_name_pos(repo->index, cp, namelen);
                if (pos < 0)
                        pos = -pos - 1;
@@ -1782,7 +1856,7 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,
                        pos++;
                }
                if (only_to_die && name[1] && name[1] != '/')
-                       diagnose_invalid_index_path(repo->index, stage, prefix, cp);
+                       diagnose_invalid_index_path(repo, stage, prefix, cp);
                free(new_path);
                return -1;
        }
@@ -1802,13 +1876,21 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,
                sub_flags &= ~GET_OID_DISAMBIGUATORS;
                sub_flags |= GET_OID_TREEISH;
 
-               if (!get_oid_1(name, len, &tree_oid, sub_flags)) {
+               if (!get_oid_1(repo, name, len, &tree_oid, sub_flags)) {
                        const char *filename = cp+1;
                        char *new_filename = NULL;
 
-                       new_filename = resolve_relative_path(filename);
+                       new_filename = resolve_relative_path(repo, filename);
                        if (new_filename)
                                filename = new_filename;
+                       /*
+                        * NEEDSWORK: Eventually get_tree_entry*() should
+                        * learn to take struct repository directly and we
+                        * would not need to inject submodule odb to the
+                        * in-core odb.
+                        */
+                       if (repo != the_repository)
+                               add_to_alternates_memory(repo->objects->odb->path);
                        if (flags & GET_OID_FOLLOW_SYMLINKS) {
                                ret = get_tree_entry_follow_symlinks(&tree_oid,
                                        filename, oid, &oc->symlink_path,
@@ -1843,11 +1925,13 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,
  * exist in 'HEAD'" when given "HEAD:doc", or it may return in which case
  * you have a chance to diagnose the error further.
  */
-void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
+void maybe_die_on_misspelt_object_name(struct repository *r,
+                                      const char *name,
+                                      const char *prefix)
 {
        struct object_context oc;
        struct object_id oid;
-       get_oid_with_context_1(the_repository, name, GET_OID_ONLY_TO_DIE,
+       get_oid_with_context_1(r, name, GET_OID_ONLY_TO_DIE,
                               prefix, &oid, &oc);
 }
 
index 32f46267710b4e88cd0fc90e1a5a6f6388361c61..f465a3cd311ea7e899ec7bf922a1c24fc831f0b4 100644 (file)
@@ -8,10 +8,10 @@
 /* opaque */
 struct git_istream;
 
-extern struct git_istream *open_istream(const struct object_id *, enum object_type *, unsigned long *, struct stream_filter *);
-extern int close_istream(struct git_istream *);
-extern ssize_t read_istream(struct git_istream *, void *, size_t);
+struct git_istream *open_istream(const struct object_id *, enum object_type *, unsigned long *, struct stream_filter *);
+int close_istream(struct git_istream *);
+ssize_t read_istream(struct git_istream *, void *, size_t);
 
-extern int stream_blob_to_fd(int fd, const struct object_id *, struct stream_filter *, int can_seek);
+int stream_blob_to_fd(int fd, const struct object_id *, struct stream_filter *, int can_seek);
 
 #endif /* STREAMING_H */
index 18c718c12ce84609ddf7d2baf471042bf9fafc7a..f964399949ce58dc1edd561fb0614776dfa2d01f 100644 (file)
@@ -174,8 +174,8 @@ struct string_list_item *string_list_insert(struct string_list *list, const char
  * Remove the given string from the sorted list.  If the string
  * doesn't exist, the list is not altered.
  */
-extern void string_list_remove(struct string_list *list, const char *string,
-                              int free_util);
+void string_list_remove(struct string_list *list, const char *string,
+                       int free_util);
 
 /**
  * Check if the given string is part of a sorted list. If it is part of the list,
index 71b18ad5af24b92421719c3e1e4d5b7154da5926..5c182fad98539eecd1b23edadebf369eb21f353b 100644 (file)
@@ -42,10 +42,10 @@ struct subprocess_capability {
 /* subprocess functions */
 
 /* Function to test two subprocess hashmap entries for equality. */
-extern int cmd2process_cmp(const void *unused_cmp_data,
-                          const void *e1,
-                          const void *e2,
-                          const void *unused_keydata);
+int cmd2process_cmp(const void *unused_cmp_data,
+                   const void *e1,
+                   const void *e2,
+                   const void *unused_keydata);
 
 /*
  * User-supplied function to initialize the sub-process.  This is
index 66653e86b9f87de01332e3d1405edd218cbf1581..4264ee216f49773c86e2e161d28ceec656a1273c 100644 (file)
@@ -625,23 +625,16 @@ static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void
                const struct config_options opts = { 0 };
                struct object_id oid;
                char *file;
+               char *oidstr = NULL;
 
                file = repo_worktree_path(repo, GITMODULES_FILE);
                if (file_exists(file)) {
                        config_source.file = file;
-               } else if (repo->submodule_prefix) {
-                       /*
-                        * When get_oid and config_with_options, used below,
-                        * become able to work on a specific repository, this
-                        * warning branch can be removed.
-                        */
-                       warning("nested submodules without %s in the working tree are not supported yet",
-                               GITMODULES_FILE);
-                       goto out;
-               } else if (get_oid(GITMODULES_INDEX, &oid) >= 0) {
-                       config_source.blob = GITMODULES_INDEX;
-               } else if (get_oid(GITMODULES_HEAD, &oid) >= 0) {
-                       config_source.blob = GITMODULES_HEAD;
+               } else if (repo_get_oid(repo, GITMODULES_INDEX, &oid) >= 0 ||
+                          repo_get_oid(repo, GITMODULES_HEAD, &oid) >= 0) {
+                       config_source.blob = oidstr = xstrdup(oid_to_hex(&oid));
+                       if (repo != the_repository)
+                               add_to_alternates_memory(repo->objects->odb->path);
                } else {
                        goto out;
                }
@@ -649,6 +642,7 @@ static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void
                config_with_options(fn, data, &config_source, &opts);
 
 out:
+               free(oidstr);
                free(file);
        }
 }
index 4dc9b0771c3c2c745d737ad228423e06d55e84c0..1b4e2da658dca189346a39bd0d7831a65cc128aa 100644 (file)
@@ -30,17 +30,17 @@ struct submodule {
 struct submodule_cache;
 struct repository;
 
-extern void submodule_cache_free(struct submodule_cache *cache);
+void submodule_cache_free(struct submodule_cache *cache);
 
-extern int parse_submodule_fetchjobs(const char *var, const char *value);
-extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
+int parse_submodule_fetchjobs(const char *var, const char *value);
+int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
 struct option;
-extern int option_fetch_parse_recurse_submodules(const struct option *opt,
-                                                const char *arg, int unset);
-extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
-extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
-extern void repo_read_gitmodules(struct repository *repo);
-extern void gitmodules_config_oid(const struct object_id *commit_oid);
+int option_fetch_parse_recurse_submodules(const struct option *opt,
+                                         const char *arg, int unset);
+int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
+int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
+void repo_read_gitmodules(struct repository *repo);
+void gitmodules_config_oid(const struct object_id *commit_oid);
 const struct submodule *submodule_from_name(struct repository *r,
                                            const struct object_id *commit_or_tree,
                                            const char *name);
@@ -66,7 +66,7 @@ int check_submodule_name(const char *name);
  * New helpers to retrieve arbitrary configuration from the '.gitmodules' file
  * should NOT be added.
  */
-extern void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules);
-extern void update_clone_config_from_gitmodules(int *max_jobs);
+void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules);
+void update_clone_config_from_gitmodules(int *max_jobs);
 
 #endif /* SUBMODULE_CONFIG_H */
diff --git a/t/helper/test-serve-v2.c b/t/helper/test-serve-v2.c
new file mode 100644 (file)
index 0000000..aee35e5
--- /dev/null
@@ -0,0 +1,31 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "serve.h"
+
+static char const * const serve_usage[] = {
+       N_("test-tool serve-v2 [<options>]"),
+       NULL
+};
+
+int cmd__serve_v2(int argc, const char **argv)
+{
+       struct serve_options opts = SERVE_OPTIONS_INIT;
+
+       struct option options[] = {
+               OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc,
+                        N_("quit after a single request/response exchange")),
+               OPT_BOOL(0, "advertise-capabilities", &opts.advertise_capabilities,
+                        N_("exit immediately after advertising capabilities")),
+               OPT_END()
+       };
+       const char *prefix = setup_git_directory();
+
+       /* ignore all unknown cmdline switches for now */
+       argc = parse_options(argc, argv, prefix, options, serve_usage,
+                            PARSE_OPT_KEEP_DASHDASH |
+                            PARSE_OPT_KEEP_UNKNOWN);
+       serve(&opts);
+
+       return 0;
+}
index 53c06932c45c17bcb2981c9b1cc081df8135dded..087a8c0cc9da64d7bc276c3870b2d0faba4c2627 100644 (file)
@@ -1,6 +1,12 @@
 #include "git-compat-util.h"
 #include "test-tool.h"
 #include "trace2.h"
+#include "parse-options.h"
+
+static const char * const test_tool_usage[] = {
+       "test-tool [-C <directory>] <command [<arguments>...]]",
+       NULL
+};
 
 struct test_cmd {
        const char *name;
@@ -43,6 +49,7 @@ static struct test_cmd cmds[] = {
        { "revision-walking", cmd__revision_walking },
        { "run-command", cmd__run_command },
        { "scrap-cache-tree", cmd__scrap_cache_tree },
+       { "serve-v2", cmd__serve_v2 },
        { "sha1", cmd__sha1 },
        { "sha1-array", cmd__sha1_array },
        { "sha256", cmd__sha256 },
@@ -75,11 +82,24 @@ static NORETURN void die_usage(void)
 int cmd_main(int argc, const char **argv)
 {
        int i;
+       const char *working_directory = NULL;
+       struct option options[] = {
+               OPT_STRING('C', NULL, &working_directory, "directory",
+                          "change the working directory"),
+               OPT_END()
+       };
 
        BUG_exit_code = 99;
+       argc = parse_options(argc, argv, NULL, options, test_tool_usage,
+                            PARSE_OPT_STOP_AT_NON_OPTION |
+                            PARSE_OPT_KEEP_ARGV0);
+
        if (argc < 2)
                die_usage();
 
+       if (working_directory && chdir(working_directory) < 0)
+               die("Could not cd to '%s'", working_directory);
+
        for (i = 0; i < ARRAY_SIZE(cmds); i++) {
                if (!strcmp(cmds[i].name, argv[1])) {
                        argv++;
index ffab4d19d701e79cac1283a749e03ae8cf437e2e..7e703f3038ae433c7d8b4ef5af51d9781d6bfffb 100644 (file)
@@ -39,6 +39,7 @@ int cmd__repository(int argc, const char **argv);
 int cmd__revision_walking(int argc, const char **argv);
 int cmd__run_command(int argc, const char **argv);
 int cmd__scrap_cache_tree(int argc, const char **argv);
+int cmd__serve_v2(int argc, const char **argv);
 int cmd__sha1(int argc, const char **argv);
 int cmd__sha1_array(int argc, const char **argv);
 int cmd__sha256(int argc, const char **argv);
index be12090c3853b8d64bb903aa92b74001d0fdd19f..c7b70e2d28ba3f8b24a3c4dfe4bb436b1f4acfa5 100644 (file)
@@ -45,7 +45,7 @@ call the aggregation script to summarize the results:
 
     $ ./p0001-rev-list.sh
     [...]
-    $ GIT_BUILD_DIR=/path/to/other/git ./p0001-rev-list.sh
+    $ ./run /path/to/other/git -- ./p0001-rev-list.sh
     [...]
     $ ./aggregate.perl . /path/to/other/git ./p0001-rev-list.sh
 
index 494907a892bba90b677448a152fd55c010b2ebda..66554d216122d26699e425b35166de460fa3821f 100755 (executable)
@@ -3,9 +3,9 @@
 use lib '../../perl/build/lib';
 use strict;
 use warnings;
-use JSON;
 use Getopt::Long;
 use Git;
+use Cwd qw(realpath);
 
 sub get_times {
        my $name = shift;
@@ -99,18 +99,21 @@ sub format_size {
 while (scalar @ARGV) {
        my $arg = $ARGV[0];
        my $dir;
+       my $prefix = '';
        last if -f $arg or $arg eq "--";
        if (! -d $arg) {
                my $rev = Git::command_oneline(qw(rev-parse --verify), $arg);
                $dir = "build/".$rev;
+       } elsif ($arg eq '.') {
+               $dir = '.';
        } else {
-               $arg =~ s{/*$}{};
-               $dir = $arg;
-               $dirabbrevs{$dir} = $dir;
+               $dir = realpath($arg);
+               $dirnames{$dir} = $dir;
+               $prefix .= 'bindir';
        }
        push @dirs, $dir;
-       $dirnames{$dir} = $arg;
-       my $prefix = $dir;
+       $dirnames{$dir} ||= $arg;
+       $prefix .= $dir;
        $prefix =~ tr/^a-zA-Z0-9/_/c;
        $prefixes{$dir} = $prefix . '.';
        shift @ARGV;
@@ -312,9 +315,6 @@ sub print_codespeed_results {
                $environment = $reponame;
        } elsif (exists $ENV{GIT_PERF_REPO_NAME} and $ENV{GIT_PERF_REPO_NAME} ne "") {
                $environment = $ENV{GIT_PERF_REPO_NAME};
-       } elsif (exists $ENV{GIT_TEST_INSTALLED} and $ENV{GIT_TEST_INSTALLED} ne "") {
-               $environment = $ENV{GIT_TEST_INSTALLED};
-               $environment =~ s|/bin-wrappers$||;
        } else {
                $environment = `uname -r`;
                chomp $environment;
@@ -342,7 +342,8 @@ sub print_codespeed_results {
                }
        }
 
-       print to_json(\@data, {utf8 => 1, pretty => 1, canonical => 1}), "\n";
+       require JSON;
+       print JSON::to_json(\@data, {utf8 => 1, pretty => 1, canonical => 1}), "\n";
 }
 
 binmode STDOUT, ":utf8" or die "PANIC on binmode: $!";
index 99bdb16c8583a98b0c33cddadabb85f47b7d5c41..a9b3e112d98223134d791727ffcde01e9123b117 100755 (executable)
@@ -13,35 +13,40 @@ test_expect_success 'repack' '
        export PACK
 '
 
-test_expect_success 'create target repositories' '
-       for repo in t1 t2 t3 t4 t5 t6
-       do
-               git init --bare $repo
-       done
-'
-
 test_perf 'index-pack 0 threads' '
-       GIT_DIR=t1 git index-pack --threads=1 --stdin < $PACK
+       rm -rf repo.git &&
+       git init --bare repo.git &&
+       GIT_DIR=repo.git git index-pack --threads=1 --stdin < $PACK
 '
 
 test_perf 'index-pack 1 thread ' '
-       GIT_DIR=t2 GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK
+       rm -rf repo.git &&
+       git init --bare repo.git &&
+       GIT_DIR=repo.git GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK
 '
 
 test_perf 'index-pack 2 threads' '
-       GIT_DIR=t3 git index-pack --threads=2 --stdin < $PACK
+       rm -rf repo.git &&
+       git init --bare repo.git &&
+       GIT_DIR=repo.git git index-pack --threads=2 --stdin < $PACK
 '
 
 test_perf 'index-pack 4 threads' '
-       GIT_DIR=t4 git index-pack --threads=4 --stdin < $PACK
+       rm -rf repo.git &&
+       git init --bare repo.git &&
+       GIT_DIR=repo.git git index-pack --threads=4 --stdin < $PACK
 '
 
 test_perf 'index-pack 8 threads' '
-       GIT_DIR=t5 git index-pack --threads=8 --stdin < $PACK
+       rm -rf repo.git &&
+       git init --bare repo.git &&
+       GIT_DIR=repo.git git index-pack --threads=8 --stdin < $PACK
 '
 
 test_perf 'index-pack default number of threads' '
-       GIT_DIR=t6 git index-pack --stdin < $PACK
+       rm -rf repo.git &&
+       git init --bare repo.git &&
+       GIT_DIR=repo.git git index-pack --stdin < $PACK
 '
 
 test_done
index bb91dbb173dd01bfbfc11f867f0fcfc355db6822..6a3a42531b056285ac3a42cabe97c41146fa4291 100755 (executable)
@@ -12,8 +12,7 @@ test_perf_large_repo
 # We intentionally use the deprecated pack.writebitmaps
 # config so that we can test against older versions of git.
 test_expect_success 'setup bitmap config' '
-       git config pack.writebitmaps true &&
-       git config pack.writebitmaphashcache true
+       git config pack.writebitmaps true
 '
 
 test_perf 'repack to disk' '
index b04575951f51ea1a41229aa7a17643aa0380dbf5..47c3fd7581cc99d562bb5796f3374e207573a304 100755 (executable)
@@ -7,7 +7,6 @@ test_perf_default_repo
 
 test_expect_success 'create bitmapped server repo' '
        git config pack.writebitmaps true &&
-       git config pack.writebitmaphashcache true &&
        git repack -ad
 '
 
diff --git a/t/perf/p5600-partial-clone.sh b/t/perf/p5600-partial-clone.sh
new file mode 100755 (executable)
index 0000000..3e04bd2
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description='performance of partial clones'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_expect_success 'enable server-side config' '
+       git config uploadpack.allowFilter true &&
+       git config uploadpack.allowAnySHA1InWant true
+'
+
+test_perf 'clone without blobs' '
+       rm -rf bare.git &&
+       git clone --no-local --bare --filter=blob:none . bare.git
+'
+
+test_perf 'checkout of result' '
+       rm -rf worktree &&
+       mkdir -p worktree/.git &&
+       tar -C bare.git -cf - . | tar -C worktree/.git -xf - &&
+       git -C worktree config core.bare false &&
+       git -C worktree checkout -f
+'
+
+test_done
index 169f92eae3d419b2d9d4ccdfbde4a394cc9e15dd..b58a43ea4364a8e5a2c5b9af39b41ec9f0b94f5b 100644 (file)
 # because it will change our working directory.
 TEST_DIRECTORY=$(pwd)/..
 TEST_OUTPUT_DIRECTORY=$(pwd)
-ABSOLUTE_GIT_TEST_INSTALLED=$(
-       test -n "$GIT_TEST_INSTALLED" && cd "$GIT_TEST_INSTALLED" && pwd)
 
 TEST_NO_CREATE_REPO=t
 TEST_NO_MALLOC_CHECK=t
 
 . ../test-lib.sh
 
-if test -z "$GIT_TEST_INSTALLED"; then
-       perf_results_prefix=
-else
-       perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"."
-       GIT_TEST_INSTALLED=$ABSOLUTE_GIT_TEST_INSTALLED
+if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED"
+then
+       error "Do not use GIT_TEST_INSTALLED with the perf tests.
+
+Instead use:
+
+    ./run <path-to-git> -- <tests>
+
+See t/perf/README for details."
 fi
 
 # Variables from test-lib that are normally internal to the tests; we
@@ -179,7 +181,7 @@ test_wrapper_ () {
                base=$(basename "$0" .sh)
                echo "$test_count" >>"$perf_results_dir"/$base.subtests
                echo "$1" >"$perf_results_dir"/$base.$test_count.descr
-               base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
+               base="$perf_results_dir"/"$PERF_RESULTS_PREFIX$(basename "$0" .sh)"."$test_count"
                "$test_wrapper_func_" "$@"
        fi
 
index 9aaa733c770fb97157e273b6c6d1efd38f9a630a..c7b86104e12a6637b1084d6e02743f6c36df9265 100755 (executable)
@@ -70,6 +70,24 @@ build_git_rev () {
        ) || die "failed to build revision '$mydir'"
 }
 
+set_git_test_installed () {
+       mydir=$1
+
+       mydir_abs=$(cd $mydir && pwd)
+       mydir_abs_wrappers="$mydir_abs_wrappers/bin-wrappers"
+       if test -d "$mydir_abs_wrappers"
+       then
+               GIT_TEST_INSTALLED=$mydir_abs_wrappers
+       else
+               # Older versions of git lacked bin-wrappers;
+               # fallback to the files in the root.
+               GIT_TEST_INSTALLED=$mydir_abs
+       fi
+       export GIT_TEST_INSTALLED
+       PERF_SET_GIT_TEST_INSTALLED=true
+       export PERF_SET_GIT_TEST_INSTALLED
+}
+
 run_dirs_helper () {
        mydir=${1%/}
        shift
@@ -79,7 +97,16 @@ run_dirs_helper () {
        if test $# -gt 0 -a "$1" = --; then
                shift
        fi
-       if [ ! -d "$mydir" ]; then
+
+       PERF_RESULTS_PREFIX=
+       if test "$mydir" = "."
+       then
+               unset GIT_TEST_INSTALLED
+       elif test -d "$mydir"
+       then
+               PERF_RESULTS_PREFIX=bindir$(cd $mydir && printf "%s" "$(pwd)" | tr -c "[a-zA-Z0-9]" "_").
+               set_git_test_installed "$mydir"
+       else
                rev=$(git rev-parse --verify "$mydir" 2>/dev/null) ||
                die "'$mydir' is neither a directory nor a valid revision"
                if [ ! -d build/$rev ]; then
@@ -87,16 +114,12 @@ run_dirs_helper () {
                fi
                build_git_rev $rev "$mydir"
                mydir=build/$rev
+
+               PERF_RESULTS_PREFIX=build_$rev.
+               set_git_test_installed "$mydir"
        fi
-       if test "$mydir" = .; then
-               unset GIT_TEST_INSTALLED
-       else
-               GIT_TEST_INSTALLED="$mydir/bin-wrappers"
-               # Older versions of git lacked bin-wrappers; fallback to the
-               # files in the root.
-               test -d "$GIT_TEST_INSTALLED" || GIT_TEST_INSTALLED=$mydir
-               export GIT_TEST_INSTALLED
-       fi
+       export PERF_RESULTS_PREFIX
+
        run_one_dir "$@"
 }
 
index 03a0aedb1d2bd11e1621e048232e22f35494acd0..71194a3623c61bd7ddf1ccfd0e2da8a3ea065dfe 100755 (executable)
@@ -3,6 +3,11 @@
 test_description='test trace2 facility (normal target)'
 . ./test-lib.sh
 
+# Turn off any inherited trace2 settings for this test.
+sane_unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
+sane_unset GIT_TR2_BRIEF
+sane_unset GIT_TR2_CONFIG_PARAMS
+
 # Add t/helper directory to PATH so that we can use a relative
 # path to run nested instances of test-tool.exe (see 004child).
 # This helps with HEREDOC comparisons later.
@@ -15,11 +20,6 @@ PATH="$TTDIR:$PATH" && export PATH
 # Warning: So you may see extra lines in artifact files when
 # Warning: interactively debugging.
 
-# Turn off any inherited trace2 settings for this test.
-unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
-unset GIT_TR2_BRIEF
-unset GIT_TR2_CONFIG_PARAMS
-
 V=$(git version | sed -e 's/^git version //') && export V
 
 # There are multiple trace2 targets: normal, perf, and event.
@@ -80,6 +80,21 @@ test_expect_success 'normal stream, return code 1' '
        test_cmp expect actual
 '
 
+test_expect_success 'automatic filename' '
+       test_when_finished "rm -r traces actual expect" &&
+       mkdir traces &&
+       GIT_TR2="$(pwd)/traces" test-tool trace2 001return 0 &&
+       perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <"$(ls traces/*)" >actual &&
+       cat >expect <<-EOF &&
+               version $V
+               start _EXE_ trace2 001return 0
+               cmd_name trace2 (trace2)
+               exit elapsed:_TIME_ code:0
+               atexit elapsed:_TIME_ code:0
+       EOF
+       test_cmp expect actual
+'
+
 # Verb 002exit
 #
 # Explicit exit(code) from within cmd_<verb> propagates <code>.
@@ -132,4 +147,43 @@ test_expect_success 'normal stream, error event' '
        test_cmp expect actual
 '
 
+sane_unset GIT_TR2_BRIEF
+
+# Now test without environment variables and get all Trace2 settings
+# from the global config.
+
+test_expect_success 'using global config, normal stream, return code 0' '
+       test_when_finished "rm trace.normal actual expect" &&
+       test_config_global trace2.normalBrief 1 &&
+       test_config_global trace2.normalTarget "$(pwd)/trace.normal" &&
+       test-tool trace2 001return 0 &&
+       perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+       cat >expect <<-EOF &&
+               version $V
+               start _EXE_ trace2 001return 0
+               cmd_name trace2 (trace2)
+               exit elapsed:_TIME_ code:0
+               atexit elapsed:_TIME_ code:0
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'using global config with include' '
+       test_when_finished "rm trace.normal actual expect real.gitconfig" &&
+       test_config_global trace2.normalBrief 1 &&
+       test_config_global trace2.normalTarget "$(pwd)/trace.normal" &&
+       mv "$(pwd)/.gitconfig" "$(pwd)/real.gitconfig" &&
+       test_config_global include.path "$(pwd)/real.gitconfig" &&
+       test-tool trace2 001return 0 &&
+       perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+       cat >expect <<-EOF &&
+               version $V
+               start _EXE_ trace2 001return 0
+               cmd_name trace2 (trace2)
+               exit elapsed:_TIME_ code:0
+               atexit elapsed:_TIME_ code:0
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 953e2f78471f647312c4b53920a4d71f24f9c4d5..b501e867af5404e8b9938c2c7ac9dfba5b8d2e30 100755 (executable)
@@ -3,6 +3,11 @@
 test_description='test trace2 facility (perf target)'
 . ./test-lib.sh
 
+# Turn off any inherited trace2 settings for this test.
+sane_unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
+sane_unset GIT_TR2_PERF_BRIEF
+sane_unset GIT_TR2_CONFIG_PARAMS
+
 # Add t/helper directory to PATH so that we can use a relative
 # path to run nested instances of test-tool.exe (see 004child).
 # This helps with HEREDOC comparisons later.
@@ -15,11 +20,6 @@ PATH="$TTDIR:$PATH" && export PATH
 # Warning: So you may see extra lines in artifact files when
 # Warning: interactively debugging.
 
-# Turn off any inherited trace2 settings for this test.
-unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
-unset GIT_TR2_PERF_BRIEF
-unset GIT_TR2_CONFIG_PARAMS
-
 V=$(git version | sed -e 's/^git version //') && export V
 
 # There are multiple trace2 targets: normal, perf, and event.
@@ -50,7 +50,7 @@ test_expect_success 'perf stream, return code 0' '
        perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
        cat >expect <<-EOF &&
                d0|main|version|||||$V
-               d0|main|start|||||_EXE_ trace2 001return 0
+               d0|main|start||_T_ABS_|||_EXE_ trace2 001return 0
                d0|main|cmd_name|||||trace2 (trace2)
                d0|main|exit||_T_ABS_|||code:0
                d0|main|atexit||_T_ABS_|||code:0
@@ -64,7 +64,7 @@ test_expect_success 'perf stream, return code 1' '
        perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
        cat >expect <<-EOF &&
                d0|main|version|||||$V
-               d0|main|start|||||_EXE_ trace2 001return 1
+               d0|main|start||_T_ABS_|||_EXE_ trace2 001return 1
                d0|main|cmd_name|||||trace2 (trace2)
                d0|main|exit||_T_ABS_|||code:1
                d0|main|atexit||_T_ABS_|||code:1
@@ -82,7 +82,7 @@ test_expect_success 'perf stream, error event' '
        perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
        cat >expect <<-EOF &&
                d0|main|version|||||$V
-               d0|main|start|||||_EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\''
+               d0|main|start||_T_ABS_|||_EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\''
                d0|main|cmd_name|||||trace2 (trace2)
                d0|main|error|||||hello world
                d0|main|error|||||this is a test
@@ -128,15 +128,15 @@ test_expect_success 'perf stream, child processes' '
        perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
        cat >expect <<-EOF &&
                d0|main|version|||||$V
-               d0|main|start|||||_EXE_ trace2 004child test-tool trace2 004child test-tool trace2 001return 0
+               d0|main|start||_T_ABS_|||_EXE_ trace2 004child test-tool trace2 004child test-tool trace2 001return 0
                d0|main|cmd_name|||||trace2 (trace2)
                d0|main|child_start||_T_ABS_|||[ch0] class:? argv: test-tool trace2 004child test-tool trace2 001return 0
                d1|main|version|||||$V
-               d1|main|start|||||_EXE_ trace2 004child test-tool trace2 001return 0
+               d1|main|start||_T_ABS_|||_EXE_ trace2 004child test-tool trace2 001return 0
                d1|main|cmd_name|||||trace2 (trace2/trace2)
                d1|main|child_start||_T_ABS_|||[ch0] class:? argv: test-tool trace2 001return 0
                d2|main|version|||||$V
-               d2|main|start|||||_EXE_ trace2 001return 0
+               d2|main|start||_T_ABS_|||_EXE_ trace2 001return 0
                d2|main|cmd_name|||||trace2 (trace2/trace2/trace2)
                d2|main|exit||_T_ABS_|||code:0
                d2|main|atexit||_T_ABS_|||code:0
@@ -150,4 +150,25 @@ test_expect_success 'perf stream, child processes' '
        test_cmp expect actual
 '
 
+sane_unset GIT_TR2_PERF_BRIEF
+
+# Now test without environment variables and get all Trace2 settings
+# from the global config.
+
+test_expect_success 'using global config, perf stream, return code 0' '
+       test_when_finished "rm trace.perf actual expect" &&
+       test_config_global trace2.perfBrief 1 &&
+       test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+       test-tool trace2 001return 0 &&
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+       cat >expect <<-EOF &&
+               d0|main|version|||||$V
+               d0|main|start||_T_ABS_|||_EXE_ trace2 001return 0
+               d0|main|cmd_name|||||trace2 (trace2)
+               d0|main|exit||_T_ABS_|||code:0
+               d0|main|atexit||_T_ABS_|||code:0
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 028b6c56715e05677d26c39cb7ade92a5510fde2..59adae8123c964b98a3b80bf26867f3562bf81b8 100755 (executable)
@@ -3,6 +3,11 @@
 test_description='test trace2 facility'
 . ./test-lib.sh
 
+# Turn off any inherited trace2 settings for this test.
+sane_unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
+sane_unset GIT_TR2_BARE
+sane_unset GIT_TR2_CONFIG_PARAMS
+
 perl -MJSON::PP -e 0 >/dev/null 2>&1 && test_set_prereq JSON_PP
 
 # Add t/helper directory to PATH so that we can use a relative
@@ -17,11 +22,6 @@ PATH="$TTDIR:$PATH" && export PATH
 # Warning: So you may see extra lines in artifact files when
 # Warning: interactively debugging.
 
-# Turn off any inherited trace2 settings for this test.
-unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
-unset GIT_TR2_BARE
-unset GIT_TR2_CONFIG_PARAMS
-
 V=$(git version | sed -e 's/^git version //') && export V
 
 # There are multiple trace2 targets: normal, perf, and event.
@@ -233,4 +233,36 @@ test_expect_success JSON_PP 'basic trace2_data' '
        test_cmp expect actual
 '
 
+# Now test without environment variables and get all Trace2 settings
+# from the global config.
+
+test_expect_success JSON_PP 'using global config, event stream, error event' '
+       test_when_finished "rm trace.event actual expect" &&
+       test_config_global trace2.eventTarget "$(pwd)/trace.event" &&
+       test-tool trace2 003error "hello world" "this is a test" &&
+       perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+       sed -e "s/^|//" >expect <<-EOF &&
+       |VAR1 = {
+       |  "_SID0_":{
+       |    "argv":[
+       |      "_EXE_",
+       |      "trace2",
+       |      "003error",
+       |      "hello world",
+       |      "this is a test"
+       |    ],
+       |    "errors":[
+       |      "%s",
+       |      "%s"
+       |    ],
+       |    "exit_code":0,
+       |    "hierarchy":"trace2",
+       |    "name":"trace2",
+       |    "version":"$V"
+       |  }
+       |};
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index a37753047e084c9be40251e7c4286084bff6870a..7099d33508a0571cd8f960fcd80c46e4364f1766 100755 (executable)
@@ -199,10 +199,6 @@ test_expect_success 'too-short tree' '
        test_i18ngrep "too-short tree object" err
 '
 
-hex2oct() {
-    perl -ne 'printf "\\%03o", hex for /../g'
-}
-
 test_expect_success 'malformed mode in tree' '
        hex_sha1=$(echo foo | git hash-object --stdin -w) &&
        bin_sha1=$(echo $hex_sha1 | hex2oct) &&
index 49f08d5b9c078412af8ee6343519094521c86c24..0f268a36642760906b59bc9a8db28a29fcb962fa 100755 (executable)
@@ -256,10 +256,6 @@ test_expect_success 'unparseable tree object' '
        test_i18ngrep ! "fatal: empty filename in tree entry" out
 '
 
-hex2oct() {
-       perl -ne 'printf "\\%03o", hex for /../g'
-}
-
 test_expect_success 'tree entry with type mismatch' '
        test_when_finished "remove_object \$blob" &&
        test_when_finished "remove_object \$tree" &&
diff --git a/t/t3009-ls-files-others-nonsubmodule.sh b/t/t3009-ls-files-others-nonsubmodule.sh
new file mode 100755 (executable)
index 0000000..963f346
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='test git ls-files --others with non-submodule repositories
+
+This test runs git ls-files --others with the following working tree:
+
+    nonrepo-no-files/
+      plain directory with no files
+    nonrepo-untracked-file/
+      plain directory with an untracked file
+    repo-no-commit-no-files/
+      git repository without a commit or a file
+    repo-no-commit-untracked-file/
+      git repository without a commit but with an untracked file
+    repo-with-commit-no-files/
+      git repository with a commit and no untracked files
+    repo-with-commit-untracked-file/
+      git repository with a commit and an untracked file
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup: directories' '
+       mkdir nonrepo-no-files/ &&
+       mkdir nonrepo-untracked-file &&
+       : >nonrepo-untracked-file/untracked &&
+       git init repo-no-commit-no-files &&
+       git init repo-no-commit-untracked-file &&
+       : >repo-no-commit-untracked-file/untracked &&
+       git init repo-with-commit-no-files &&
+       git -C repo-with-commit-no-files commit --allow-empty -mmsg &&
+       git init repo-with-commit-untracked-file &&
+       test_commit -C repo-with-commit-untracked-file msg &&
+       : >repo-with-commit-untracked-file/untracked
+'
+
+test_expect_success 'ls-files --others handles untracked git repositories' '
+       git ls-files -o >output &&
+       cat >expect <<-EOF &&
+       nonrepo-untracked-file/untracked
+       output
+       repo-no-commit-no-files/
+       repo-no-commit-untracked-file/
+       repo-with-commit-no-files/
+       repo-with-commit-untracked-file/
+       EOF
+       test_cmp expect output
+'
+
+test_done
index 478b82cf9b513a8f45f250de59d519847227f794..e9ad50b66dece121464f0638d5a4a1dd0280d121 100755 (executable)
@@ -264,6 +264,30 @@ test_expect_success 'git branch --list -d t should fail' '
        test_must_fail git rev-parse refs/heads/t
 '
 
+test_expect_success 'deleting checked-out branch from repo that is a submodule' '
+       test_when_finished "rm -rf repo1 repo2" &&
+
+       git init repo1 &&
+       git init repo1/sub &&
+       test_commit -C repo1/sub x &&
+       git -C repo1 submodule add ./sub &&
+       git -C repo1 commit -m "adding sub" &&
+
+       git clone --recurse-submodules repo1 repo2 &&
+       git -C repo2/sub checkout -b work &&
+       test_must_fail git -C repo2/sub branch -D work
+'
+
+test_expect_success 'bare main worktree has HEAD at branch deleted by secondary worktree' '
+       test_when_finished "rm -rf nonbare base secondary" &&
+
+       git init nonbare &&
+       test_commit -C nonbare x &&
+       git clone --bare nonbare bare &&
+       git -C bare worktree add --detach ../secondary master &&
+       git -C secondary branch -D master
+'
+
 test_expect_success 'git branch --list -v with --abbrev' '
        test_when_finished "git branch -D t" &&
        git branch t &&
index e0b5111993e741a22c9d92f9cbe6b7a9ce731267..a0b9438b2286712c41fb0d5648e8bf29caee8842 100755 (executable)
@@ -42,7 +42,7 @@ test_expect_success 'rebase --interactive: directory rename detected' '
                git checkout B^0 &&
 
                set_fake_editor &&
-               FAKE_LINES="1" git rebase --interactive A &&
+               FAKE_LINES="1" git -c merge.directoryRenames=true rebase --interactive A &&
 
                git ls-files -s >out &&
                test_line_count = 5 out &&
@@ -58,7 +58,7 @@ test_expect_failure 'rebase (am): directory rename detected' '
 
                git checkout B^0 &&
 
-               git rebase A &&
+               git -c merge.directoryRenames=true rebase A &&
 
                git ls-files -s >out &&
                test_line_count = 5 out &&
@@ -74,7 +74,7 @@ test_expect_success 'rebase --merge: directory rename detected' '
 
                git checkout B^0 &&
 
-               git rebase --merge A &&
+               git -c merge.directoryRenames=true rebase --merge A &&
 
                git ls-files -s >out &&
                test_line_count = 5 out &&
@@ -92,7 +92,7 @@ test_expect_failure 'am: directory rename detected' '
 
                git format-patch -1 B &&
 
-               git am --3way 0001*.patch &&
+               git -c merge.directoryRenames=true am --3way 0001*.patch &&
 
                git ls-files -s >out &&
                test_line_count = 5 out &&
index 1ef8e9d534eae78014d9cccebc5f827a3cef91f3..9b9b4ca8d4f2a2188c206a0e1e2de763d00a1ab6 100755 (executable)
@@ -93,7 +93,7 @@ test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' '
 
 test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '
        pristine_detach initial &&
-       echo foo > foo &&
+       echo foo >foo &&
        test_must_fail git cherry-pick base &&
        test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
 '
@@ -101,7 +101,7 @@ test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '
 test_expect_success \
        'cherry-pick --strategy=resolve w/dirty tree does not set CHERRY_PICK_HEAD' '
        pristine_detach initial &&
-       echo foo > foo &&
+       echo foo >foo &&
        test_must_fail git cherry-pick --strategy=resolve base &&
        test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
 '
@@ -161,6 +161,25 @@ test_expect_success 'successful commit clears CHERRY_PICK_HEAD' '
 
        test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
 '
+test_expect_success 'successful final commit clears cherry-pick state' '
+       pristine_detach initial &&
+
+       test_must_fail git cherry-pick base picked-signed &&
+       echo resolved >foo &&
+       test_path_is_file .git/sequencer/todo &&
+       git commit -a &&
+       test_must_fail test_path_exists .git/sequencer
+'
+
+test_expect_success 'reset after final pick clears cherry-pick state' '
+       pristine_detach initial &&
+
+       test_must_fail git cherry-pick base picked-signed &&
+       echo resolved >foo &&
+       test_path_is_file .git/sequencer/todo &&
+       git reset &&
+       test_must_fail test_path_exists .git/sequencer
+'
 
 test_expect_success 'failed cherry-pick produces dirty index' '
        pristine_detach initial &&
@@ -180,23 +199,63 @@ test_expect_success 'failed cherry-pick registers participants in index' '
                git ls-files --stage foo &&
                git checkout picked -- foo &&
                git ls-files --stage foo
-       } > stages &&
+       } >stages &&
        sed "
                1 s/ 0  / 1     /
                2 s/ 0  / 2     /
                3 s/ 0  / 3     /
-       " < stages > expected &&
+       " stages >expected &&
        git read-tree -u --reset HEAD &&
 
        test_must_fail git cherry-pick picked &&
-       git ls-files --stage --unmerged > actual &&
+       git ls-files --stage --unmerged >actual &&
 
        test_cmp expected actual
 '
 
+test_expect_success \
+       'cherry-pick conflict, ensure commit.cleanup = scissors places scissors line properly' '
+       pristine_detach initial &&
+       git config commit.cleanup scissors &&
+       cat <<-EOF >expected &&
+               picked
+
+               # ------------------------ >8 ------------------------
+               # Do not modify or remove the line above.
+               # Everything below it will be ignored.
+               #
+               # Conflicts:
+               #       foo
+               EOF
+
+       test_must_fail git cherry-pick picked &&
+
+       test_i18ncmp expected .git/MERGE_MSG
+'
+
+test_expect_success \
+       'cherry-pick conflict, ensure cleanup=scissors places scissors line properly' '
+       pristine_detach initial &&
+       git config --unset commit.cleanup &&
+       cat <<-EOF >expected &&
+               picked
+
+               # ------------------------ >8 ------------------------
+               # Do not modify or remove the line above.
+               # Everything below it will be ignored.
+               #
+               # Conflicts:
+               #       foo
+               EOF
+
+       test_must_fail git cherry-pick --cleanup=scissors picked &&
+
+       test_i18ncmp expected .git/MERGE_MSG
+'
+
 test_expect_success 'failed cherry-pick describes conflict in work tree' '
        pristine_detach initial &&
-       cat <<-EOF > expected &&
+       cat <<-EOF >expected &&
        <<<<<<< HEAD
        a
        =======
@@ -206,14 +265,14 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '
 
        test_must_fail git cherry-pick picked &&
 
-       sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+       sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
        test_cmp expected actual
 '
 
 test_expect_success 'diff3 -m style' '
        pristine_detach initial &&
        git config merge.conflictstyle diff3 &&
-       cat <<-EOF > expected &&
+       cat <<-EOF >expected &&
        <<<<<<< HEAD
        a
        ||||||| parent of objid picked
@@ -225,14 +284,14 @@ test_expect_success 'diff3 -m style' '
 
        test_must_fail git cherry-pick picked &&
 
-       sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+       sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
        test_cmp expected actual
 '
 
 test_expect_success 'revert also handles conflicts sanely' '
        git config --unset merge.conflictstyle &&
        pristine_detach initial &&
-       cat <<-EOF > expected &&
+       cat <<-EOF >expected &&
        <<<<<<< HEAD
        a
        =======
@@ -246,24 +305,24 @@ test_expect_success 'revert also handles conflicts sanely' '
                git ls-files --stage foo &&
                git checkout base -- foo &&
                git ls-files --stage foo
-       } > stages &&
+       } >stages &&
        sed "
                1 s/ 0  / 1     /
                2 s/ 0  / 2     /
                3 s/ 0  / 3     /
-       " < stages > expected-stages &&
+       " stages >expected-stages &&
        git read-tree -u --reset HEAD &&
 
        head=$(git rev-parse HEAD) &&
        test_must_fail git revert picked &&
        newhead=$(git rev-parse HEAD) &&
-       git ls-files --stage --unmerged > actual-stages &&
+       git ls-files --stage --unmerged >actual-stages &&
 
        test "$head" = "$newhead" &&
        test_must_fail git update-index --refresh -q &&
        test_must_fail git diff-index --exit-code HEAD &&
        test_cmp expected-stages actual-stages &&
-       sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+       sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
        test_cmp expected actual
 '
 
@@ -289,7 +348,7 @@ test_expect_success 'revert --no-commit sets REVERT_HEAD' '
 
 test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '
        pristine_detach base &&
-       echo foo > foo &&
+       echo foo >foo &&
        test_must_fail git revert base &&
        test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
        test_must_fail git rev-parse --verify REVERT_HEAD
@@ -321,10 +380,30 @@ test_expect_success 'failed commit does not clear REVERT_HEAD' '
        test_cmp_rev picked REVERT_HEAD
 '
 
+test_expect_success 'successful final commit clears revert state' '
+       pristine_detach picked-signed &&
+
+       test_must_fail git revert picked-signed base &&
+       echo resolved >foo &&
+       test_path_is_file .git/sequencer/todo &&
+       git commit -a &&
+       test_must_fail test_path_exists .git/sequencer
+'
+
+test_expect_success 'reset after final pick clears revert state' '
+       pristine_detach picked-signed &&
+
+       test_must_fail git revert picked-signed base &&
+       echo resolved >foo &&
+       test_path_is_file .git/sequencer/todo &&
+       git reset &&
+       test_must_fail test_path_exists .git/sequencer
+'
+
 test_expect_success 'revert conflict, diff3 -m style' '
        pristine_detach initial &&
        git config merge.conflictstyle diff3 &&
-       cat <<-EOF > expected &&
+       cat <<-EOF >expected &&
        <<<<<<< HEAD
        a
        ||||||| objid picked
@@ -336,10 +415,56 @@ test_expect_success 'revert conflict, diff3 -m style' '
 
        test_must_fail git revert picked &&
 
-       sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+       sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
        test_cmp expected actual
 '
 
+test_expect_success \
+       'revert conflict, ensure commit.cleanup = scissors places scissors line properly' '
+       pristine_detach initial &&
+       git config commit.cleanup scissors &&
+       cat >expected <<-EOF &&
+               Revert "picked"
+
+               This reverts commit OBJID.
+
+               # ------------------------ >8 ------------------------
+               # Do not modify or remove the line above.
+               # Everything below it will be ignored.
+               #
+               # Conflicts:
+               #       foo
+               EOF
+
+       test_must_fail git revert picked &&
+
+       sed "s/$OID_REGEX/OBJID/" .git/MERGE_MSG >actual &&
+       test_i18ncmp expected actual
+'
+
+test_expect_success \
+       'revert conflict, ensure cleanup=scissors places scissors line properly' '
+       pristine_detach initial &&
+       git config --unset commit.cleanup &&
+       cat >expected <<-EOF &&
+               Revert "picked"
+
+               This reverts commit OBJID.
+
+               # ------------------------ >8 ------------------------
+               # Do not modify or remove the line above.
+               # Everything below it will be ignored.
+               #
+               # Conflicts:
+               #       foo
+               EOF
+
+       test_must_fail git revert --cleanup=scissors picked &&
+
+       sed "s/$OID_REGEX/OBJID/" .git/MERGE_MSG >actual &&
+       test_i18ncmp expected actual
+'
+
 test_expect_success 'failed cherry-pick does not forget -s' '
        pristine_detach initial &&
        test_must_fail git cherry-pick -s picked &&
@@ -350,7 +475,7 @@ test_expect_success 'commit after failed cherry-pick does not add duplicated -s'
        pristine_detach initial &&
        test_must_fail git cherry-pick -s picked-signed &&
        git commit -a -s &&
-       test $(git show -s |grep -c "Signed-off-by") = 1
+       test $(git show -s >tmp && grep -c "Signed-off-by" tmp && rm tmp) = 1
 '
 
 test_expect_success 'commit after failed cherry-pick adds -s at the right place' '
@@ -364,7 +489,7 @@ test_expect_success 'commit after failed cherry-pick adds -s at the right place'
        Signed-off-by: C O Mitter <committer@example.com>
        # Conflicts:
        EOF
-       grep -e "^# Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
+       grep -e "^# Conflicts:" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&
        test_cmp expect actual &&
 
        cat <<-\EOF >expected &&
@@ -383,7 +508,7 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' '
 
        # emulate old-style conflicts block
        mv .git/MERGE_MSG .git/MERGE_MSG+ &&
-       sed -e "/^# Conflicts:/,\$s/^# *//" <.git/MERGE_MSG+ >.git/MERGE_MSG &&
+       sed -e "/^# Conflicts:/,\$s/^# *//" .git/MERGE_MSG+ >.git/MERGE_MSG &&
 
        git commit -a &&
        git commit --amend -s &&
@@ -393,7 +518,7 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' '
        Signed-off-by: C O Mitter <committer@example.com>
        Conflicts:
        EOF
-       grep -e "^Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
+       grep -e "^Conflicts:" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&
        test_cmp expect actual
 '
 
index 9888bf34b9538f6e4aae1a1c65022ae126a62ce2..84a587daf3af0356244c58c6472e202875679200 100755 (executable)
@@ -298,4 +298,24 @@ test_expect_success 'cherry-pick preserves commit message' '
        test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick -x cleans commit message' '
+       pristine_detach initial &&
+       git cherry-pick -x mesg-unclean &&
+       git log -1 --pretty=format:%B >actual &&
+       printf "%s\n(cherry picked from commit %s)\n" \
+               "$mesg_unclean" $(git rev-parse mesg-unclean) |
+                       git stripspace >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x respects commit.cleanup' '
+       pristine_detach initial &&
+       git -c commit.cleanup=strip cherry-pick -x mesg-unclean &&
+       git log -1 --pretty=format:%B >actual &&
+       printf "%s\n(cherry picked from commit %s)\n" \
+               "$mesg_unclean" $(git rev-parse mesg-unclean) |
+                       git stripspace -s >expect &&
+       test_cmp expect actual
+'
+
 test_done
index be582a513b1b99a71c10279aacc72cb36bd4bf7b..c325167b90318b2f85a9b53e3aea89eb989e6d64 100755 (executable)
@@ -296,6 +296,17 @@ test_expect_success '"git add ." in empty repo' '
        )
 '
 
+test_expect_success 'error on a repository with no commits' '
+       rm -fr empty &&
+       git init empty &&
+       test_must_fail git add empty >actual 2>&1 &&
+       cat >expect <<-EOF &&
+       error: '"'empty/'"' does not have a commit checked out
+       fatal: adding files failed
+       EOF
+       test_i18ncmp expect actual
+'
+
 test_expect_success 'git add --dry-run of existing changed file' "
        echo new >>track-this &&
        git add --dry-run track-this >actual 2>&1 &&
@@ -396,6 +407,7 @@ test_expect_success 'no file status change if no pathspec is given in subdir' '
 '
 
 test_expect_success 'all statuses changed in folder if . is given' '
+       rm -fr empty &&
        git add --chmod=+x . &&
        test $(git ls-files --stage | grep ^100644 | wc -l) -eq 0 &&
        git add --chmod=-x . &&
index 1eeb828a15146e9e5040c64b20a31329853f12e7..df60f18fb8caa252a173603258ed09f9a3c2f89d 100755 (executable)
@@ -341,4 +341,12 @@ test_expect_success 'prune: handle expire option correctly' '
        git prune --no-expire
 '
 
+test_expect_success 'trivial prune with bitmaps enabled' '
+       git repack -adb &&
+       blob=$(echo bitmap-unreachable-blob | git hash-object -w --stdin) &&
+       git prune --expire=now &&
+       git cat-file -e HEAD &&
+       test_must_fail git cat-file -e $blob
+'
+
 test_done
index 82d7f7f6a5bbdc5609e0b76c97a3ae14f6a53a92..a26c8ba9a2b1b815e1917f703cb3520a7608d624 100755 (executable)
@@ -34,8 +34,7 @@ test_expect_success 'setup repo with moderate-sized history' '
        bitmaptip=$(git rev-parse master) &&
        blob=$(echo tagged-blob | git hash-object -w --stdin) &&
        git tag tagged-blob $blob &&
-       git config repack.writebitmaps true &&
-       git config pack.writebitmaphashcache true
+       git config repack.writebitmaps true
 '
 
 test_expect_success 'full repack creates bitmaps' '
@@ -269,7 +268,7 @@ test_expect_success JGIT 'we can read jgit bitmaps' '
        git clone --bare . compat-jgit.git &&
        (
                cd compat-jgit.git &&
-               rm -f .git/objects/pack/*.bitmap &&
+               rm -f objects/pack/*.bitmap &&
                jgit gc &&
                git rev-list --test-bitmap HEAD
        )
index c19d8dbc9d916463d51dc0903185cb50a6cb5af7..ccde8ba491e8f119dc48accf5c10f533bf74c2fd 100755 (executable)
@@ -77,6 +77,14 @@ test_expect_success 'git pull -q -v' '
        test_must_be_empty out &&
        test -s err)
 '
+test_expect_success 'git pull --cleanup errors early on invalid argument' '
+       mkdir clonedcleanup &&
+       (cd clonedcleanup && git init &&
+       test_must_fail git pull --cleanup invalid "../parent" >out 2>err &&
+       test_must_be_empty out &&
+       test -s err)
+'
+
 
 test_expect_success 'git pull --force' '
        mkdir clonedoldstyle &&
index 23854cab263d467ba189879add1fb6b408e00792..de9d99cf88a5b72a3aa6e190124dff36e67818b6 100755 (executable)
@@ -611,10 +611,6 @@ test_expect_success 'GIT_TRACE_PACKFILE produces a usable pack' '
        git -C replay.git index-pack -v --stdin <tmp.pack
 '
 
-hex2oct () {
-       perl -ne 'printf "\\%03o", hex for /../g'
-}
-
 test_expect_success 'clone on case-insensitive fs' '
        git init icasefs &&
        (
index fe45bf828d09cd9d95a66742ef938837d35141a3..ffb9613885904eaf474a726f6124032d13db9753 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='test git-serve and server commands'
+test_description='test protocol v2 server commands'
 
 . ./test-lib.sh
 
@@ -14,7 +14,8 @@ test_expect_success 'test capability advertisement' '
        0000
        EOF
 
-       GIT_TEST_SIDEBAND_ALL=0 git serve --advertise-capabilities >out &&
+       GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
+               --advertise-capabilities >out &&
        test-tool pkt-line unpack <out >actual &&
        test_cmp expect actual
 '
@@ -24,11 +25,11 @@ test_expect_success 'stateless-rpc flag does not list capabilities' '
        test-tool pkt-line pack >in <<-EOF &&
        0000
        EOF
-       git serve --stateless-rpc >out <in &&
+       test-tool serve-v2 --stateless-rpc >out <in &&
        test_must_be_empty out &&
 
        # EOF
-       git serve --stateless-rpc >out &&
+       test-tool serve-v2 --stateless-rpc >out &&
        test_must_be_empty out
 '
 
@@ -37,7 +38,7 @@ test_expect_success 'request invalid capability' '
        foobar
        0000
        EOF
-       test_must_fail git serve --stateless-rpc 2>err <in &&
+       test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
        test_i18ngrep "unknown capability" err
 '
 
@@ -46,7 +47,7 @@ test_expect_success 'request with no command' '
        agent=git/test
        0000
        EOF
-       test_must_fail git serve --stateless-rpc 2>err <in &&
+       test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
        test_i18ngrep "no command requested" err
 '
 
@@ -56,7 +57,7 @@ test_expect_success 'request invalid command' '
        agent=git/test
        0000
        EOF
-       test_must_fail git serve --stateless-rpc 2>err <in &&
+       test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
        test_i18ngrep "invalid command" err
 '
 
@@ -87,7 +88,7 @@ test_expect_success 'basics of ls-refs' '
        0000
        EOF
 
-       git serve --stateless-rpc <in >out &&
+       test-tool serve-v2 --stateless-rpc <in >out &&
        test-tool pkt-line unpack <out >actual &&
        test_cmp expect actual
 '
@@ -107,7 +108,7 @@ test_expect_success 'basic ref-prefixes' '
        0000
        EOF
 
-       git serve --stateless-rpc <in >out &&
+       test-tool serve-v2 --stateless-rpc <in >out &&
        test-tool pkt-line unpack <out >actual &&
        test_cmp expect actual
 '
@@ -127,7 +128,7 @@ test_expect_success 'refs/heads prefix' '
        0000
        EOF
 
-       git serve --stateless-rpc <in >out &&
+       test-tool serve-v2 --stateless-rpc <in >out &&
        test-tool pkt-line unpack <out >actual &&
        test_cmp expect actual
 '
@@ -148,7 +149,7 @@ test_expect_success 'peel parameter' '
        0000
        EOF
 
-       git serve --stateless-rpc <in >out &&
+       test-tool serve-v2 --stateless-rpc <in >out &&
        test-tool pkt-line unpack <out >actual &&
        test_cmp expect actual
 '
@@ -169,7 +170,7 @@ test_expect_success 'symrefs parameter' '
        0000
        EOF
 
-       git serve --stateless-rpc <in >out &&
+       test-tool serve-v2 --stateless-rpc <in >out &&
        test-tool pkt-line unpack <out >actual &&
        test_cmp expect actual
 '
@@ -189,7 +190,7 @@ test_expect_success 'sending server-options' '
        0000
        EOF
 
-       git serve --stateless-rpc <in >out &&
+       test-tool serve-v2 --stateless-rpc <in >out &&
        test-tool pkt-line unpack <out >actual &&
        test_cmp expect actual
 '
@@ -204,7 +205,10 @@ test_expect_success 'unexpected lines are not allowed in fetch request' '
        0000
        EOF
 
-       test_must_fail git -C server serve --stateless-rpc <in >/dev/null 2>err &&
+       (
+               cd server &&
+               test_must_fail test-tool serve-v2 --stateless-rpc
+       ) <in >/dev/null 2>err &&
        grep "unexpected line: .this-is-not-a-command." err
 '
 
index a0896593375803eb79782705a21d673c72172c83..5b33f625dddeccce7ecd68b45f115a46f7fbe93a 100755 (executable)
@@ -182,6 +182,13 @@ test_expect_success 'server-options are sent when using ls-remote' '
        grep "server-option=world" log
 '
 
+test_expect_success 'warn if using server-option with ls-remote with legacy protocol' '
+       test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -c protocol.version=0 \
+               ls-remote -o hello -o world "file://$(pwd)/file_parent" master 2>err &&
+
+       test_i18ngrep "see protocol.version in" err &&
+       test_i18ngrep "server options require protocol version 2 or later" err
+'
 
 test_expect_success 'clone with file:// using protocol v2' '
        test_when_finished "rm -f log" &&
@@ -251,6 +258,40 @@ test_expect_success 'server-options are sent when fetching' '
        grep "server-option=world" log
 '
 
+test_expect_success 'warn if using server-option with fetch with legacy protocol' '
+       test_when_finished "rm -rf temp_child" &&
+
+       git init temp_child &&
+
+       test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -C temp_child -c protocol.version=0 \
+               fetch -o hello -o world "file://$(pwd)/file_parent" master 2>err &&
+
+       test_i18ngrep "see protocol.version in" err &&
+       test_i18ngrep "server options require protocol version 2 or later" err
+'
+
+test_expect_success 'server-options are sent when cloning' '
+       test_when_finished "rm -rf log myclone" &&
+
+       GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
+               clone --server-option=hello --server-option=world \
+               "file://$(pwd)/file_parent" myclone &&
+
+       grep "server-option=hello" log &&
+       grep "server-option=world" log
+'
+
+test_expect_success 'warn if using server-option with clone with legacy protocol' '
+       test_when_finished "rm -rf myclone" &&
+
+       test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -c protocol.version=0 \
+               clone --server-option=hello --server-option=world \
+               "file://$(pwd)/file_parent" myclone 2>err &&
+
+       test_i18ngrep "see protocol.version in" err &&
+       test_i18ngrep "server options require protocol version 2 or later" err
+'
+
 test_expect_success 'upload-pack respects config using protocol v2' '
        git init server &&
        write_script server/.git/hook <<-\EOF &&
@@ -359,12 +400,13 @@ test_expect_success 'even with handcrafted request, filter does not work if not
        0000
        EOF
 
-       test_must_fail git -C server serve --stateless-rpc <in >/dev/null 2>err &&
+       test_must_fail test-tool -C server serve-v2 --stateless-rpc \
+               <in >/dev/null 2>err &&
        grep "unexpected line: .filter blob:none." err &&
 
        # Exercise to ensure that if advertised, filter works
        git -C server config uploadpack.allowfilter 1 &&
-       git -C server serve --stateless-rpc <in >/dev/null
+       test-tool -C server serve-v2 --stateless-rpc <in >/dev/null
 '
 
 test_expect_success 'default refspec is used to filter ref when fetchcing' '
index b6a995e8579c91874d2850ca579f87bf93f40561..0951d1bbdc26d998cdca7ed491eca557d53b2ddf 100755 (executable)
@@ -48,15 +48,15 @@ test_expect_success 'setup repository' '
 '
 
 test_expect_success 'config controls ref-in-want advertisement' '
-       git serve --advertise-capabilities >out &&
+       test-tool serve-v2 --advertise-capabilities >out &&
        ! grep -a ref-in-want out &&
 
        git config uploadpack.allowRefInWant false &&
-       git serve --advertise-capabilities >out &&
+       test-tool serve-v2 --advertise-capabilities >out &&
        ! grep -a ref-in-want out &&
 
        git config uploadpack.allowRefInWant true &&
-       git serve --advertise-capabilities >out &&
+       test-tool serve-v2 --advertise-capabilities >out &&
        grep -a ref-in-want out
 '
 
@@ -70,7 +70,7 @@ test_expect_success 'invalid want-ref line' '
        0000
        EOF
 
-       test_must_fail git serve --stateless-rpc 2>out <in &&
+       test_must_fail test-tool serve-v2 --stateless-rpc 2>out <in &&
        grep "unknown ref" out
 '
 
@@ -90,7 +90,7 @@ test_expect_success 'basic want-ref' '
        0000
        EOF
 
-       git serve --stateless-rpc >out <in &&
+       test-tool serve-v2 --stateless-rpc >out <in &&
        check_output
 '
 
@@ -112,7 +112,7 @@ test_expect_success 'multiple want-ref lines' '
        0000
        EOF
 
-       git serve --stateless-rpc >out <in &&
+       test-tool serve-v2 --stateless-rpc >out <in &&
        check_output
 '
 
@@ -133,7 +133,7 @@ test_expect_success 'mix want and want-ref' '
        0000
        EOF
 
-       git serve --stateless-rpc >out <in &&
+       test-tool serve-v2 --stateless-rpc >out <in &&
        check_output
 '
 
@@ -153,7 +153,7 @@ test_expect_success 'want-ref with ref we already have commit for' '
        0000
        EOF
 
-       git serve --stateless-rpc >out <in &&
+       test-tool serve-v2 --stateless-rpc >out <in &&
        check_output
 '
 
index aaaa722ccab016cc5847d5b6bbc5a3879a0b3fbc..d04f8007e0e34fa938bf4fccb91eff384743e616 100755 (executable)
@@ -8,6 +8,8 @@ test_description='Test remote-helper import and export commands'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
+PATH="$TEST_DIRECTORY/t5801:$PATH"
+
 compare_refs() {
        git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
        git --git-dir="$3/.git" rev-parse --verify $4 >actual &&
diff --git a/t/t5801/git-remote-testgit b/t/t5801/git-remote-testgit
new file mode 100755 (executable)
index 0000000..752c763
--- /dev/null
@@ -0,0 +1,147 @@
+#!/bin/sh
+# Copyright (c) 2012 Felipe Contreras
+
+# The first argument can be a url when the fetch/push command was a url
+# instead of a configured remote. In this case, use a generic alias.
+if test "$1" = "testgit::$2"; then
+       alias=_
+else
+       alias=$1
+fi
+url=$2
+
+dir="$GIT_DIR/testgit/$alias"
+prefix="refs/testgit/$alias"
+
+default_refspec="refs/heads/*:${prefix}/heads/*"
+
+refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}"
+
+test -z "$refspec" && prefix="refs"
+
+GIT_DIR="$url/.git"
+export GIT_DIR
+
+force=
+
+mkdir -p "$dir"
+
+if test -z "$GIT_REMOTE_TESTGIT_NO_MARKS"
+then
+       gitmarks="$dir/git.marks"
+       testgitmarks="$dir/testgit.marks"
+       test -e "$gitmarks" || >"$gitmarks"
+       test -e "$testgitmarks" || >"$testgitmarks"
+fi
+
+while read line
+do
+       case $line in
+       capabilities)
+               echo 'import'
+               echo 'export'
+               test -n "$refspec" && echo "refspec $refspec"
+               if test -n "$gitmarks"
+               then
+                       echo "*import-marks $gitmarks"
+                       echo "*export-marks $gitmarks"
+               fi
+               test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags"
+               test -n "$GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE" && echo "no-private-update"
+               echo 'option'
+               echo
+               ;;
+       list)
+               git for-each-ref --format='? %(refname)' 'refs/heads/'
+               head=$(git symbolic-ref HEAD)
+               echo "@$head HEAD"
+               echo
+               ;;
+       import*)
+               # read all import lines
+               while true
+               do
+                       ref="${line#* }"
+                       refs="$refs $ref"
+                       read line
+                       test "${line%% *}" != "import" && break
+               done
+
+               if test -n "$gitmarks"
+               then
+                       echo "feature import-marks=$gitmarks"
+                       echo "feature export-marks=$gitmarks"
+               fi
+
+               if test -n "$GIT_REMOTE_TESTGIT_FAILURE"
+               then
+                       echo "feature done"
+                       exit 1
+               fi
+
+               echo "feature done"
+               git fast-export \
+                       ${testgitmarks:+"--import-marks=$testgitmarks"} \
+                       ${testgitmarks:+"--export-marks=$testgitmarks"} \
+                       $refs |
+               sed -e "s#refs/heads/#${prefix}/heads/#g"
+               echo "done"
+               ;;
+       export)
+               if test -n "$GIT_REMOTE_TESTGIT_FAILURE"
+               then
+                       # consume input so fast-export doesn't get SIGPIPE;
+                       # git would also notice that case, but we want
+                       # to make sure we are exercising the later
+                       # error checks
+                       while read line; do
+                               test "done" = "$line" && break
+                       done
+                       exit 1
+               fi
+
+               before=$(git for-each-ref --format=' %(refname) %(objectname) ')
+
+               git fast-import \
+                       ${force:+--force} \
+                       ${testgitmarks:+"--import-marks=$testgitmarks"} \
+                       ${testgitmarks:+"--export-marks=$testgitmarks"} \
+                       --quiet
+
+               # figure out which refs were updated
+               git for-each-ref --format='%(refname) %(objectname)' |
+               while read ref a
+               do
+                       case "$before" in
+                       *" $ref $a "*)
+                               continue ;;     # unchanged
+                       esac
+                       if test -z "$GIT_REMOTE_TESTGIT_PUSH_ERROR"
+                       then
+                               echo "ok $ref"
+                       else
+                               echo "error $ref $GIT_REMOTE_TESTGIT_PUSH_ERROR"
+                       fi
+               done
+
+               echo
+               ;;
+       option\ *)
+               read cmd opt val <<-EOF
+               $line
+               EOF
+               case $opt in
+               force)
+                       test $val = "true" && force="true" || force=
+                       echo "ok"
+                       ;;
+               *)
+                       echo "unsupported"
+                       ;;
+               esac
+               ;;
+       '')
+               exit
+               ;;
+       esac
+done
index 62c564707b9ff8a8e86b1f8d2d2e3919c36f1b5f..50b7543483e9988f3a97d7782e11504f82558af4 100755 (executable)
@@ -75,7 +75,7 @@ test_expect_success '1a-check: Simple directory rename detection' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 
                git ls-files -s >out &&
                test_line_count = 4 out &&
@@ -142,7 +142,7 @@ test_expect_success '1b-check: Merge a directory with another' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 4 out &&
@@ -201,7 +201,7 @@ test_expect_success '1c-check: Transitive renaming' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -270,7 +270,7 @@ test_expect_success '1d-check: Directory renames cause a rename/rename(2to1) con
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (rename/rename)" out &&
 
                git ls-files -s >out &&
@@ -350,7 +350,7 @@ test_expect_success '1e-check: Renamed directory, with all files being renamed t
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -416,7 +416,7 @@ test_expect_success '1f-check: Split a directory into two other directories' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 6 out &&
@@ -497,7 +497,7 @@ test_expect_success '2a-check: Directory split into two on one side, with equal
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT.*directory rename split" out &&
 
                git ls-files -s >out &&
@@ -559,7 +559,7 @@ test_expect_success '2b-check: Directory split into two on one side, with equal
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 >out &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -640,7 +640,7 @@ test_expect_success '3a-check: Avoid implicit rename if involved as source on ot
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -705,7 +705,7 @@ test_expect_success '3b-check: Avoid implicit rename if involved as source on cu
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
                test_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&
 
@@ -826,7 +826,7 @@ test_expect_success '4a-check: Directory split, with original directory still pr
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 5 out &&
@@ -915,7 +915,7 @@ test_expect_success '5a-check: Merge directories, other side adds files to origi
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT.*implicit dir rename" out &&
 
                git ls-files -s >out &&
@@ -989,7 +989,7 @@ test_expect_success '5b-check: Rename/delete in order to get add/add/add conflic
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (add/add).* y/d" out &&
 
                git ls-files -s >out &&
@@ -1069,7 +1069,7 @@ test_expect_success '5c-check: Transitive rename would cause rename/rename/renam
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
                test_i18ngrep "CONFLICT (add/add).* y/d" out &&
 
@@ -1153,7 +1153,7 @@ test_expect_success '5d-check: Directory/file/file conflict due to directory ren
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (file/directory).*y/d" out &&
 
                git ls-files -s >out &&
@@ -1243,7 +1243,7 @@ test_expect_success '6a-check: Tricky rename/delete' '
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (rename/delete).*z/c.*y/c" out &&
 
                git ls-files -s >out &&
@@ -1308,7 +1308,7 @@ test_expect_success '6b-check: Same rename done on both sides' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -1370,7 +1370,7 @@ test_expect_success '6c-check: Rename only done on same side' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -1432,7 +1432,7 @@ test_expect_success '6d-check: We do not always want transitive renaming' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -1495,7 +1495,7 @@ test_expect_success '6e-check: Add/add from one side' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 4 out &&
@@ -1591,7 +1591,7 @@ test_expect_success '7a-check: rename-dir vs. rename-dir (NOT split evenly) PLUS
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&
                test_i18ngrep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&
 
@@ -1663,7 +1663,7 @@ test_expect_success '7b-check: rename/rename(2to1), but only due to transitive r
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (rename/rename)" out &&
 
                git ls-files -s >out &&
@@ -1740,7 +1740,7 @@ test_expect_success '7c-check: rename/rename(1to...2or3); transitive rename may
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&
 
                git ls-files -s >out &&
@@ -1804,7 +1804,7 @@ test_expect_success '7d-check: transitive rename involved in rename/delete; how
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
 
                git ls-files -s >out &&
@@ -1894,7 +1894,7 @@ test_expect_success '7e-check: transitive rename in rename/delete AND dirs in th
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
 
                git ls-files -s >out &&
@@ -1985,7 +1985,7 @@ test_expect_success '8a-check: Dual-directory rename, one into the others way' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 6 out &&
@@ -2063,7 +2063,7 @@ test_expect_success '8b-check: Dual-directory rename, one into the others way, w
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 6 out &&
@@ -2135,7 +2135,7 @@ test_expect_success '8c-check: modify/delete or rename+modify/delete' '
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "CONFLICT (modify/delete).* z/d" out &&
 
                git ls-files -s >out &&
@@ -2212,7 +2212,7 @@ test_expect_success '8d-check: rename/delete...or not?' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -2287,7 +2287,7 @@ test_expect_success '8e-check: Both sides rename, one side adds to original dire
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&
                test_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&
 
@@ -2374,7 +2374,7 @@ test_expect_success '9a-check: Inner renamed directory within outer renamed dire
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 7 out &&
@@ -2444,7 +2444,7 @@ test_expect_success '9b-check: Transitive rename with content merge' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -2534,7 +2534,7 @@ test_expect_success '9c-check: Doubly transitive rename?' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 >out &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "WARNING: Avoiding applying x -> z rename to x/f" out &&
 
                git ls-files -s >out &&
@@ -2622,7 +2622,7 @@ test_expect_success '9d-check: N-way transitive rename?' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 >out &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                test_i18ngrep "WARNING: Avoiding applying z -> y rename to z/t" out &&
                test_i18ngrep "WARNING: Avoiding applying y -> x rename to y/a" out &&
                test_i18ngrep "WARNING: Avoiding applying x -> w rename to x/b" out &&
@@ -2704,7 +2704,7 @@ test_expect_success C_LOCALE_OUTPUT '9e-check: N-to-1 whammo' '
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 >out &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
                grep "CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo" out >error_line &&
                grep -q dir1/yo error_line &&
                grep -q dir2/yo error_line &&
@@ -2782,7 +2782,7 @@ test_expect_success '9f-check: Renamed directory that only contained immediate s
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 4 out &&
@@ -2849,7 +2849,7 @@ test_expect_failure '9g-check: Renamed directory that only contained immediate s
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 4 out &&
@@ -2918,7 +2918,7 @@ test_expect_success '9h-check: Avoid dir rename on merely modified path' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 3 out &&
@@ -2993,7 +2993,7 @@ test_expect_success '10a-check: Overwrite untracked with normal rename/delete' '
                echo very >z/c &&
                echo important >z/d &&
 
-               test_must_fail git merge -s recursive B^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep "The following untracked working tree files would be overwritten by merge" err &&
 
                git ls-files -s >out &&
@@ -3061,7 +3061,7 @@ test_expect_success '10b-check: Overwrite untracked with dir rename + delete' '
                echo important >y/d &&
                echo contents >y/e &&
 
-               test_must_fail git merge -s recursive B^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
                test_i18ngrep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
 
@@ -3137,7 +3137,7 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)
                git checkout A^0 &&
                echo important >y/c &&
 
-               test_must_fail git merge -s recursive B^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep "CONFLICT (rename/rename)" out &&
                test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
 
@@ -3174,7 +3174,7 @@ test_expect_success '10c-check: Overwrite untracked with dir rename/rename(1to2)
                mkdir y &&
                echo important >y/c &&
 
-               test_must_fail git merge -s recursive A^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
                test_i18ngrep "CONFLICT (rename/rename)" out &&
                test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
 
@@ -3249,7 +3249,7 @@ test_expect_success '10d-check: Delete untracked with dir rename/rename(2to1)' '
                git checkout A^0 &&
                echo important >y/wham &&
 
-               test_must_fail git merge -s recursive B^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep "CONFLICT (rename/rename)" out &&
                test_i18ngrep "Refusing to lose untracked file at y/wham" out &&
 
@@ -3327,7 +3327,7 @@ test_expect_failure '10e-check: Does git complain about untracked file that is n
                mkdir z &&
                echo random >z/c &&
 
-               git merge -s recursive B^0 >out 2>err &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err &&
 
                git ls-files -s >out &&
@@ -3407,7 +3407,7 @@ test_expect_success '11a-check: Avoid losing dirty contents with simple rename'
                git checkout A^0 &&
                echo stuff >>z/c &&
 
-               test_must_fail git merge -s recursive B^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep "Refusing to lose dirty file at z/c" out &&
 
                test_seq 1 10 >expected &&
@@ -3479,7 +3479,7 @@ test_expect_success '11b-check: Avoid losing dirty file involved in directory re
                git checkout A^0 &&
                echo stuff >>z/c &&
 
-               git merge -s recursive B^0 >out 2>err &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep "Refusing to lose dirty file at z/c" out &&
 
                grep -q stuff z/c &&
@@ -3554,7 +3554,7 @@ test_expect_success '11c-check: Avoid losing not-uptodate with rename + D/F conf
                git checkout A^0 &&
                echo stuff >>y/c &&
 
-               test_must_fail git merge -s recursive B^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep "following files would be overwritten by merge" err &&
 
                grep -q stuff y/c &&
@@ -3621,7 +3621,7 @@ test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conf
                git checkout A^0 &&
                echo stuff >>z/c &&
 
-               test_must_fail git merge -s recursive B^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep "Refusing to lose dirty file at z/c" out &&
 
                grep -q stuff z/c &&
@@ -3700,7 +3700,7 @@ test_expect_success '11e-check: Avoid deleting not-uptodate with dir rename/rena
                git checkout A^0 &&
                echo mods >>y/c &&
 
-               test_must_fail git merge -s recursive B^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep "CONFLICT (rename/rename)" out &&
                test_i18ngrep "Refusing to lose dirty file at y/c" out &&
 
@@ -3782,7 +3782,7 @@ test_expect_success '11f-check: Avoid deleting not-uptodate with dir rename/rena
                git checkout A^0 &&
                echo important >>y/wham &&
 
-               test_must_fail git merge -s recursive B^0 >out 2>err &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
                test_i18ngrep "CONFLICT (rename/rename)" out &&
                test_i18ngrep "Refusing to lose dirty file at y/wham" out &&
 
@@ -3870,7 +3870,7 @@ test_expect_success '12a-check: Moving one directory hierarchy into another' '
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 6 out &&
@@ -3910,7 +3910,7 @@ test_expect_success '12a-check: Moving one directory hierarchy into another' '
 #         To which, I can do no more than shrug my shoulders and say that
 #         even simple rules give weird results when given weird inputs.
 
-test_expect_success '12b-setup: Moving one directory hierarchy into another' '
+test_expect_success '12b-setup: Moving two directory hierarchies into each other' '
        test_create_repo 12b &&
        (
                cd 12b &&
@@ -3940,13 +3940,13 @@ test_expect_success '12b-setup: Moving one directory hierarchy into another' '
        )
 '
 
-test_expect_success '12b-check: Moving one directory hierarchy into another' '
+test_expect_success '12b-check: Moving two directory hierarchies into each other' '
        (
                cd 12b &&
 
                git checkout A^0 &&
 
-               git merge -s recursive B^0 &&
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
                test_line_count = 4 out &&
@@ -4016,7 +4016,7 @@ test_expect_success '12c-check: Moving one directory hierarchy into another w/ c
 
                git checkout A^0 &&
 
-               test_must_fail git merge -s recursive B^0 &&
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -u >out &&
                test_line_count = 12 out &&
@@ -4051,4 +4051,356 @@ test_expect_success '12c-check: Moving one directory hierarchy into another w/ c
        )
 '
 
+###########################################################################
+# SECTION 13: Checking informational and conflict messages
+#
+# A year after directory rename detection became the default, it was
+# instead decided to report conflicts on the pathname on the basis that
+# some users may expect the new files added or moved into a directory to
+# be unrelated to all the other files in that directory, and thus that
+# directory rename detection is unexpected.  Test that the messages printed
+# match our expectation.
+###########################################################################
+
+# Testcase 13a, Basic directory rename with newly added files
+#   Commit O: z/{b,c}
+#   Commit A: y/{b,c}
+#   Commit B: z/{b,c,d,e/f}
+#   Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f
+
+test_expect_success '13a-setup: messages for newly added files' '
+       test_create_repo 13a &&
+       (
+               cd 13a &&
+
+               mkdir z &&
+               echo b >z/b &&
+               echo c >z/c &&
+               git add z &&
+               test_tick &&
+               git commit -m "O" &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git checkout A &&
+               git mv z y &&
+               test_tick &&
+               git commit -m "A" &&
+
+               git checkout B &&
+               echo d >z/d &&
+               mkdir z/e &&
+               echo f >z/e/f &&
+               git add z/d z/e/f &&
+               test_tick &&
+               git commit -m "B"
+       )
+'
+
+test_expect_success '13a-check(conflict): messages for newly added files' '
+       (
+               cd 13a &&
+
+               git checkout A^0 &&
+
+               test_must_fail git merge -s recursive B^0 >out 2>err &&
+
+               test_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&
+               test_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&
+
+               git ls-files >paths &&
+               ! grep z/ paths &&
+               grep "y/[de]" paths &&
+
+               test_path_is_missing z/d &&
+               test_path_is_file    y/d &&
+               test_path_is_missing z/e/f &&
+               test_path_is_file    y/e/f
+       )
+'
+
+test_expect_success '13a-check(info): messages for newly added files' '
+       (
+               cd 13a &&
+
+               git reset --hard &&
+               git checkout A^0 &&
+
+               git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
+
+               test_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&
+               test_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out &&
+
+               git ls-files >paths &&
+               ! grep z/ paths &&
+               grep "y/[de]" paths &&
+
+               test_path_is_missing z/d &&
+               test_path_is_file    y/d &&
+               test_path_is_missing z/e/f &&
+               test_path_is_file    y/e/f
+       )
+'
+
+# Testcase 13b, Transitive rename with conflicted content merge and default
+#               "conflict" setting
+#   (Related to testcase 1c, 9b)
+#   Commit O: z/{b,c},   x/d_1
+#   Commit A: y/{b,c},   x/d_2
+#   Commit B: z/{b,c,d_3}
+#   Expected: y/{b,c,d_merged}, with two conflict messages for y/d,
+#             one about content, and one about file location
+
+test_expect_success '13b-setup: messages for transitive rename with conflicted content' '
+       test_create_repo 13b &&
+       (
+               cd 13b &&
+
+               mkdir x &&
+               mkdir z &&
+               test_seq 1 10 >x/d &&
+               echo b >z/b &&
+               echo c >z/c &&
+               git add x z &&
+               test_tick &&
+               git commit -m "O" &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git checkout A &&
+               git mv z y &&
+               echo 11 >>x/d &&
+               git add x/d &&
+               test_tick &&
+               git commit -m "A" &&
+
+               git checkout B &&
+               echo eleven >>x/d &&
+               git mv x/d z/d &&
+               git add z/d &&
+               test_tick &&
+               git commit -m "B"
+       )
+'
+
+test_expect_success '13b-check(conflict): messages for transitive rename with conflicted content' '
+       (
+               cd 13b &&
+
+               git checkout A^0 &&
+
+               test_must_fail git merge -s recursive B^0 >out 2>err &&
+
+               test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
+               test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
+
+               git ls-files >paths &&
+               ! grep z/ paths &&
+               grep "y/d" paths &&
+
+               test_path_is_missing z/d &&
+               test_path_is_file    y/d
+       )
+'
+
+test_expect_success '13b-check(info): messages for transitive rename with conflicted content' '
+       (
+               cd 13b &&
+
+               git reset --hard &&
+               git checkout A^0 &&
+
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
+
+               test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
+               test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
+
+               git ls-files >paths &&
+               ! grep z/ paths &&
+               grep "y/d" paths &&
+
+               test_path_is_missing z/d &&
+               test_path_is_file    y/d
+       )
+'
+
+# Testcase 13c, Rename/rename(1to1) due to directory rename
+#   Commit O: z/{b,c},   x/{d,e}
+#   Commit A: y/{b,c,d}, x/e
+#   Commit B: z/{b,c,d}, x/e
+#   Expected: y/{b,c,d}, with info or conflict messages for d (
+#             A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d
+#             One could argue A had partial knowledge of what was done with
+#             d and B had full knowledge, but that's a slippery slope as
+#             shown in testcase 13d.
+
+test_expect_success '13c-setup: messages for rename/rename(1to1) via transitive rename' '
+       test_create_repo 13c &&
+       (
+               cd 13c &&
+
+               mkdir x &&
+               mkdir z &&
+               test_seq 1 10 >x/d &&
+               echo e >x/e &&
+               echo b >z/b &&
+               echo c >z/c &&
+               git add x z &&
+               test_tick &&
+               git commit -m "O" &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git checkout A &&
+               git mv z y &&
+               git mv x/d y/ &&
+               test_tick &&
+               git commit -m "A" &&
+
+               git checkout B &&
+               git mv x/d z/d &&
+               git add z/d &&
+               test_tick &&
+               git commit -m "B"
+       )
+'
+
+test_expect_success '13c-check(conflict): messages for rename/rename(1to1) via transitive rename' '
+       (
+               cd 13c &&
+
+               git checkout A^0 &&
+
+               test_must_fail git merge -s recursive B^0 >out 2>err &&
+
+               test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
+
+               git ls-files >paths &&
+               ! grep z/ paths &&
+               grep "y/d" paths &&
+
+               test_path_is_missing z/d &&
+               test_path_is_file    y/d
+       )
+'
+
+test_expect_success '13c-check(info): messages for rename/rename(1to1) via transitive rename' '
+       (
+               cd 13c &&
+
+               git reset --hard &&
+               git checkout A^0 &&
+
+               git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
+
+               test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
+
+               git ls-files >paths &&
+               ! grep z/ paths &&
+               grep "y/d" paths &&
+
+               test_path_is_missing z/d &&
+               test_path_is_file    y/d
+       )
+'
+
+# Testcase 13d, Rename/rename(1to1) due to directory rename on both sides
+#   Commit O: a/{z,y}, b/x,     c/w
+#   Commit A: a/z,     b/{y,x}, d/w
+#   Commit B: a/z,     d/x,     c/{y,w}
+#   Expected: a/z, d/{y,x,w} with no file location conflict for x
+#             Easy cases:
+#               * z is always in a; so it stays in a.
+#               * x starts in b, only modified on one side to move into d/
+#               * w starts in c, only modified on one side to move into d/
+#             Hard case:
+#               * A renames a/y to b/y, and B renames b/->d/ => a/y -> d/y
+#               * B renames a/y to c/y, and A renames c/->d/ => a/y -> d/y
+#               No conflict in where a/y ends up, so put it in d/y.
+
+test_expect_success '13d-setup: messages for rename/rename(1to1) via dual transitive rename' '
+       test_create_repo 13d &&
+       (
+               cd 13d &&
+
+               mkdir a &&
+               mkdir b &&
+               mkdir c &&
+               echo z >a/z &&
+               echo y >a/y &&
+               echo x >b/x &&
+               echo w >c/w &&
+               git add a b c &&
+               test_tick &&
+               git commit -m "O" &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git checkout A &&
+               git mv a/y b/ &&
+               git mv c/ d/ &&
+               test_tick &&
+               git commit -m "A" &&
+
+               git checkout B &&
+               git mv a/y c/ &&
+               git mv b/ d/ &&
+               test_tick &&
+               git commit -m "B"
+       )
+'
+
+test_expect_success '13d-check(conflict): messages for rename/rename(1to1) via dual transitive rename' '
+       (
+               cd 13d &&
+
+               git checkout A^0 &&
+
+               test_must_fail git merge -s recursive B^0 >out 2>err &&
+
+               test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&
+               test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&
+
+               git ls-files >paths &&
+               ! grep b/ paths &&
+               ! grep c/ paths &&
+               grep "d/y" paths &&
+
+               test_path_is_missing b/y &&
+               test_path_is_missing c/y &&
+               test_path_is_file    d/y
+       )
+'
+
+test_expect_success '13d-check(info): messages for rename/rename(1to1) via dual transitive rename' '
+       (
+               cd 13d &&
+
+               git reset --hard &&
+               git checkout A^0 &&
+
+               git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
+
+               test_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&
+               test_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&
+
+               git ls-files >paths &&
+               ! grep b/ paths &&
+               ! grep c/ paths &&
+               grep "d/y" paths &&
+
+               test_path_is_missing b/y &&
+               test_path_is_missing c/y &&
+               test_path_is_file    d/y
+       )
+'
+
 test_done
index 38e24f787cffb5e645a5b31c8bfa30fb5106bea7..3a47623ed3160b9037cbf7fd63bf7638a8dd57dc 100755 (executable)
@@ -466,7 +466,7 @@ test_expect_success '3a-check-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
 
                git checkout A^0 &&
 
-               GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
+               GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
                test_i18ngrep ! "Skipped bar/bq" out &&
                test_must_be_empty err &&
@@ -495,7 +495,7 @@ test_expect_success '3a-check-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
 
                git checkout B^0 &&
 
-               GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
+               GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
 
                test_i18ngrep ! "Skipped bar/bq" out &&
                test_must_be_empty err &&
@@ -560,7 +560,7 @@ test_expect_success '3b-check-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
 
                git checkout A^0 &&
 
-               GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
+               GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
                test_i18ngrep ! "Skipped bar/bq" out &&
                test_must_be_empty err &&
@@ -589,7 +589,7 @@ test_expect_success '3b-check-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
 
                git checkout B^0 &&
 
-               GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
+               GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
 
                test_i18ngrep ! "Skipped bar/bq" out &&
                test_must_be_empty err &&
index d638119750c5b9f11682814de466cc19a9d38770..e7e64e085ddcfe7d7cf4df844472a6bf456ab079 100755 (executable)
@@ -40,7 +40,8 @@ commit_peeling_shows_parents ()
        test "$_found" = "$_parent" || return 1
        _parent_number=$(( $_parent_number + 1 ))
     done &&
-    test_must_fail git rev-parse --verify $_commit^$_parent_number
+    test_must_fail git rev-parse --verify $_commit^$_parent_number 2>err &&
+    test_i18ngrep "Needed a single revision" err
 }
 
 commit_has_parents ()
@@ -393,9 +394,11 @@ test_expect_success 'replace ref cleanup' '
 '
 
 test_expect_success '--graft with and without already replaced object' '
-       test $(git log --oneline | wc -l) = 7 &&
+       git log --oneline >log &&
+       test_line_count = 7 log &&
        git replace --graft $HASH5 &&
-       test $(git log --oneline | wc -l) = 3 &&
+       git log --oneline >log &&
+       test_line_count = 3 log &&
        commit_has_parents $HASH5 &&
        test_must_fail git replace --graft $HASH5 $HASH4 $HASH3 &&
        git replace --force -g $HASH5 $HASH4 $HASH3 &&
@@ -403,6 +406,28 @@ test_expect_success '--graft with and without already replaced object' '
        git replace -d $HASH5
 '
 
+test_expect_success '--graft using a tag as the new parent' '
+       git tag new_parent $HASH5 &&
+       git replace --graft $HASH7 new_parent &&
+       commit_has_parents $HASH7 $HASH5 &&
+       git replace -d $HASH7 &&
+       git tag -a -m "annotated new parent tag" annotated_new_parent $HASH5 &&
+       git replace --graft $HASH7 annotated_new_parent &&
+       commit_has_parents $HASH7 $HASH5 &&
+       git replace -d $HASH7
+'
+
+test_expect_success '--graft using a tag as the replaced object' '
+       git tag replaced_object $HASH7 &&
+       git replace --graft replaced_object $HASH5 &&
+       commit_has_parents $HASH7 $HASH5 &&
+       git replace -d $HASH7 &&
+       git tag -a -m "annotated replaced object tag" annotated_replaced_object $HASH7 &&
+       git replace --graft annotated_replaced_object $HASH5 &&
+       commit_has_parents $HASH7 $HASH5 &&
+       git replace -d $HASH7
+'
+
 test_expect_success GPG 'set up a signed commit' '
        echo "line 17" >>hello &&
        echo "line 18" >>hello &&
diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh
new file mode 100755 (executable)
index 0000000..28611c9
--- /dev/null
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+test_description='git rev-list should handle unexpected object types'
+
+. ./test-lib.sh
+
+test_expect_success 'setup well-formed objects' '
+       blob="$(printf "foo" | git hash-object -w --stdin)" &&
+       tree="$(printf "100644 blob $blob\tfoo" | git mktree)" &&
+       commit="$(git commit-tree $tree -m "first commit")" &&
+       git cat-file commit $commit >good-commit
+'
+
+test_expect_success 'setup unexpected non-blob entry' '
+       printf "100644 foo\0$(echo $tree | hex2oct)" >broken-tree &&
+       broken_tree="$(git hash-object -w --literally -t tree broken-tree)"
+'
+
+test_expect_failure 'traverse unexpected non-blob entry (lone)' '
+       test_must_fail git rev-list --objects $broken_tree
+'
+
+test_expect_success 'traverse unexpected non-blob entry (seen)' '
+       test_must_fail git rev-list --objects $tree $broken_tree >output 2>&1 &&
+       test_i18ngrep "is not a blob" output
+'
+
+test_expect_success 'setup unexpected non-tree entry' '
+       printf "40000 foo\0$(echo $blob | hex2oct)" >broken-tree &&
+       broken_tree="$(git hash-object -w --literally -t tree broken-tree)"
+'
+
+test_expect_success 'traverse unexpected non-tree entry (lone)' '
+       test_must_fail git rev-list --objects $broken_tree
+'
+
+test_expect_success 'traverse unexpected non-tree entry (seen)' '
+       test_must_fail git rev-list --objects $blob $broken_tree >output 2>&1 &&
+       test_i18ngrep "is not a tree" output
+'
+
+test_expect_success 'setup unexpected non-commit parent' '
+       sed "/^author/ { h; s/.*/parent $blob/; G; }" <good-commit \
+               >broken-commit &&
+       broken_commit="$(git hash-object -w --literally -t commit \
+               broken-commit)"
+'
+
+test_expect_success 'traverse unexpected non-commit parent (lone)' '
+       test_must_fail git rev-list --objects $broken_commit >output 2>&1 &&
+       test_i18ngrep "not a commit" output
+'
+
+test_expect_success 'traverse unexpected non-commit parent (seen)' '
+       test_must_fail git rev-list --objects $commit $broken_commit \
+               >output 2>&1 &&
+       test_i18ngrep "not a commit" output
+'
+
+test_expect_success 'setup unexpected non-tree root' '
+       sed -e "s/$tree/$blob/" <good-commit >broken-commit &&
+       broken_commit="$(git hash-object -w --literally -t commit \
+               broken-commit)"
+'
+
+test_expect_success 'traverse unexpected non-tree root (lone)' '
+       test_must_fail git rev-list --objects $broken_commit
+'
+
+test_expect_success 'traverse unexpected non-tree root (seen)' '
+       test_must_fail git rev-list --objects $blob $broken_commit \
+               >output 2>&1 &&
+       test_i18ngrep "not a tree" output
+'
+
+test_expect_success 'setup unexpected non-commit tag' '
+       git tag -a -m "tagged commit" tag $commit &&
+       git cat-file tag tag >good-tag &&
+       test_when_finished "git tag -d tag" &&
+       sed -e "s/$commit/$blob/" <good-tag >broken-tag &&
+       tag=$(git hash-object -w --literally -t tag broken-tag)
+'
+
+test_expect_success 'traverse unexpected non-commit tag (lone)' '
+       test_must_fail git rev-list --objects $tag
+'
+
+test_expect_success 'traverse unexpected non-commit tag (seen)' '
+       test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
+       test_i18ngrep "not a commit" output
+'
+
+test_expect_success 'setup unexpected non-tree tag' '
+       git tag -a -m "tagged tree" tag $tree &&
+       git cat-file tag tag >good-tag &&
+       test_when_finished "git tag -d tag" &&
+       sed -e "s/$tree/$blob/" <good-tag >broken-tag &&
+       tag=$(git hash-object -w --literally -t tag broken-tag)
+'
+
+test_expect_success 'traverse unexpected non-tree tag (lone)' '
+       test_must_fail git rev-list --objects $tag
+'
+
+test_expect_success 'traverse unexpected non-tree tag (seen)' '
+       test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
+       test_i18ngrep "not a tree" output
+'
+
+test_expect_success 'setup unexpected non-blob tag' '
+       git tag -a -m "tagged blob" tag $blob &&
+       git cat-file tag tag >good-tag &&
+       test_when_finished "git tag -d tag" &&
+       sed -e "s/$blob/$commit/" <good-tag >broken-tag &&
+       tag=$(git hash-object -w --literally -t tag broken-tag)
+'
+
+test_expect_failure 'traverse unexpected non-blob tag (lone)' '
+       test_must_fail git rev-list --objects $tag
+'
+
+test_expect_success 'traverse unexpected non-blob tag (seen)' '
+       test_must_fail git rev-list --objects $commit $tag >output 2>&1 &&
+       test_i18ngrep "not a blob" output
+'
+
+test_done
index 0ffd63071392eba0cd960ddf50d45bc0b5f1d5ea..d9235217fcc72912574c80cea684aa17c8b736d2 100755 (executable)
@@ -392,8 +392,15 @@ test_atom head upstream:track '[ahead 1]'
 test_atom head upstream:trackshort '>'
 test_atom head upstream:track,nobracket 'ahead 1'
 test_atom head upstream:nobracket,track 'ahead 1'
-test_atom head push:track '[ahead 1]'
-test_atom head push:trackshort '>'
+
+test_expect_success 'setup for push:track[short]' '
+       test_commit third &&
+       git update-ref refs/remotes/myfork/master master &&
+       git reset master~1
+'
+
+test_atom head push:track '[behind 1]'
+test_atom head push:trackshort '<'
 
 test_expect_success 'Check that :track[short] cannot be used with other atoms' '
        test_must_fail git for-each-ref --format="%(refname:track)" 2>/dev/null &&
@@ -420,8 +427,10 @@ test_expect_success 'Check for invalid refname format' '
 test_expect_success 'set up color tests' '
        cat >expected.color <<-EOF &&
        $(git rev-parse --short refs/heads/master) <GREEN>master<RESET>
+       $(git rev-parse --short refs/remotes/myfork/master) <GREEN>myfork/master<RESET>
        $(git rev-parse --short refs/remotes/origin/master) <GREEN>origin/master<RESET>
        $(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>
+       $(git rev-parse --short refs/tags/third) <GREEN>third<RESET>
        $(git rev-parse --short refs/tags/two) <GREEN>two<RESET>
        EOF
        sed "s/<[^>]*>//g" <expected.color >expected.bare &&
index 0b01862c23aa182ee4ae7ce2944a40b4449d6049..6aeeb279a0a03614151a37f1bed8f22f4af0c4f5 100755 (executable)
@@ -1700,6 +1700,17 @@ test_expect_success '--points-at finds annotated tags of tags' '
        test_cmp expect actual
 '
 
+test_expect_success 'recursive tagging should give advice' '
+       sed -e "s/|$//" <<-EOF >expect &&
+       hint: You have created a nested tag. The object referred to by your new tag is
+       hint: already a tag. If you meant to tag the object that it points to, use:
+       hint: |
+       hint:   git tag -f nested annotated-v4.0^{}
+       EOF
+       git tag -m nested nested annotated-v4.0 2>actual &&
+       test_i18ncmp expect actual
+'
+
 test_expect_success 'multiple --points-at are OR-ed together' '
        cat >expect <<-\EOF &&
        v2.0
index aba2d4d6ee415ab5528ed67d518cfb26412f1142..a208cb26e1dfd4b1a147f620747c025600ce7181 100755 (executable)
@@ -46,6 +46,15 @@ test_expect_success 'submodule update aborts on missing gitmodules url' '
        test_must_fail git submodule init
 '
 
+test_expect_success 'add aborts on repository with no commits' '
+       cat >expect <<-\EOF &&
+       '"'repo-no-commits'"' does not have a commit checked out
+       EOF
+       git init repo-no-commits &&
+       test_must_fail git submodule add ../a ./repo-no-commits 2>actual &&
+       test_i18ncmp expect actual
+'
+
 test_expect_success 'setup - repository in init subdirectory' '
        mkdir init &&
        (
@@ -809,7 +818,7 @@ test_expect_success '../bar/a/b/c works with relative local path - ../foo/bar.gi
                cp pristine-.git-config .git/config &&
                cp pristine-.gitmodules .gitmodules &&
                mkdir -p a/b/c &&
-               (cd a/b/c && git init) &&
+               (cd a/b/c && git init && test_commit msg) &&
                git config remote.origin.url ../foo/bar.git &&
                git submodule add ../bar/a/b/c ./a/b/c &&
                git submodule init &&
index ca4a740da0258b5522f29051c3d18632d1bafa1b..5733d9cd3462a2448b3bb748e62dfe8452f9a733 100755 (executable)
@@ -16,7 +16,8 @@ commit_msg_is () {
 # Arguments: [<prefix] [<commit message>] [<commit options>]
 check_summary_oneline() {
        test_tick &&
-       git commit ${3+"$3"} -m "$2" | head -1 > act &&
+       git commit ${3+"$3"} -m "$2" >raw &&
+       head -n 1 raw >act &&
 
        # branch name
        SUMMARY_PREFIX="$(git name-rev --name-only HEAD)" &&
@@ -68,7 +69,7 @@ test_expect_success 'output summary format for merges' '
        git checkout recursive-a &&
        test_must_fail git merge recursive-b &&
        # resolve the conflict
-       echo commit-a > file1 &&
+       echo commit-a >file1 &&
        git add file1 &&
        check_summary_oneline "" "Merge"
 '
@@ -142,9 +143,11 @@ test_expect_success 'sign off' '
        >positive &&
        git add positive &&
        git commit -s -m "thank you" &&
-       actual=$(git cat-file commit HEAD | sed -ne "s/Signed-off-by: //p") &&
-       expected=$(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/") &&
-       test "z$actual" = "z$expected"
+       git cat-file commit HEAD >commit.msg &&
+       sed -ne "s/Signed-off-by: //p" commit.msg >actual &&
+       git var GIT_COMMITTER_IDENT >ident &&
+       sed -e "s/>.*/>/" ident >expected &&
+       test_cmp expected actual
 
 '
 
@@ -153,8 +156,8 @@ test_expect_success 'multiple -m' '
        >negative &&
        git add negative &&
        git commit -m "one" -m "two" -m "three" &&
-       actual=$(git cat-file commit HEAD | sed -e "1,/^\$/d") &&
-       expected=$(echo one; echo; echo two; echo; echo three) &&
+       actual=$(git cat-file commit HEAD >tmp && sed -e "1,/^\$/d" tmp && rm tmp) &&
+       expected=$(test_write_lines "one" "" "two" "" "three") &&
        test "z$actual" = "z$expected"
 
 '
@@ -163,7 +166,8 @@ test_expect_success 'verbose' '
 
        echo minus >negative &&
        git add negative &&
-       git status -v | sed -ne "/^diff --git /p" >actual &&
+       git status -v >raw &&
+       sed -ne "/^diff --git /p" raw >actual &&
        echo "diff --git a/negative b/negative" >expect &&
        test_cmp expect actual
 
@@ -189,7 +193,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-t)' '
 
        echo >>negative &&
        git commit --cleanup=verbatim --no-status -t expect -a &&
-       git cat-file -p HEAD |sed -e "1,/^\$/d" >actual &&
+       git cat-file -p HEAD >raw &&
+       sed -e "1,/^\$/d" raw >actual &&
        test_cmp expect actual
 
 '
@@ -198,7 +203,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-F)' '
 
        echo >>negative &&
        git commit --cleanup=verbatim -F expect -a &&
-       git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+       git cat-file -p HEAD >raw &&
+       sed -e "1,/^\$/d" raw >actual &&
        test_cmp expect actual
 
 '
@@ -207,7 +213,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-m)' '
 
        echo >>negative &&
        git commit --cleanup=verbatim -m "$mesg_with_comment_and_newlines" -a &&
-       git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+       git cat-file -p HEAD >raw &&
+       sed -e "1,/^\$/d" raw >actual &&
        test_cmp expect actual
 
 '
@@ -215,10 +222,11 @@ test_expect_success 'cleanup commit messages (verbatim option,-m)' '
 test_expect_success 'cleanup commit messages (whitespace option,-F)' '
 
        echo >>negative &&
-       { echo;echo "# text";echo; } >text &&
+       test_write_lines "" "# text" "" >text &&
        echo "# text" >expect &&
        git commit --cleanup=whitespace -F text -a &&
-       git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+       git cat-file -p HEAD >raw &&
+       sed -e "1,/^\$/d" raw >actual &&
        test_cmp expect actual
 
 '
@@ -226,48 +234,51 @@ test_expect_success 'cleanup commit messages (whitespace option,-F)' '
 test_expect_success 'cleanup commit messages (scissors option,-F,-e)' '
 
        echo >>negative &&
-       cat >text <<EOF &&
+       cat >text <<-\EOF &&
 
-# to be kept
+       # to be kept
 
-  # ------------------------ >8 ------------------------
-# to be kept, too
-# ------------------------ >8 ------------------------
-to be removed
-# ------------------------ >8 ------------------------
-to be removed, too
-EOF
+         # ------------------------ >8 ------------------------
+       # to be kept, too
+       # ------------------------ >8 ------------------------
+       to be removed
+       # ------------------------ >8 ------------------------
+       to be removed, too
+       EOF
 
-       cat >expect <<EOF &&
-# to be kept
+       cat >expect <<-\EOF &&
+       # to be kept
 
-  # ------------------------ >8 ------------------------
-# to be kept, too
-EOF
+         # ------------------------ >8 ------------------------
+       # to be kept, too
+       EOF
        git commit --cleanup=scissors -e -F text -a &&
-       git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+       git cat-file -p HEAD >raw &&
+       sed -e "1,/^\$/d" raw >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on first line)' '
 
        echo >>negative &&
-       cat >text <<EOF &&
-# ------------------------ >8 ------------------------
-to be removed
-EOF
+       cat >text <<-\EOF &&
+       # ------------------------ >8 ------------------------
+       to be removed
+       EOF
        git commit --cleanup=scissors -e -F text -a --allow-empty-message &&
-       git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+       git cat-file -p HEAD >raw &&
+       sed -e "1,/^\$/d" raw >actual &&
        test_must_be_empty actual
 '
 
 test_expect_success 'cleanup commit messages (strip option,-F)' '
 
        echo >>negative &&
-       { echo;echo "# text";echo sample;echo; } >text &&
+       test_write_lines "" "# text" "sample" "" >text &&
        echo sample >expect &&
        git commit --cleanup=strip -F text -a &&
-       git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+       git cat-file -p HEAD >raw &&
+       sed -e "1,/^\$/d" raw >actual &&
        test_cmp expect actual
 
 '
@@ -275,7 +286,7 @@ test_expect_success 'cleanup commit messages (strip option,-F)' '
 test_expect_success 'cleanup commit messages (strip option,-F,-e)' '
 
        echo >>negative &&
-       { echo;echo sample;echo; } >text &&
+       test_write_lines "" "sample" "" >text &&
        git commit -e -F text -a &&
        head -n 4 .git/COMMIT_EDITMSG >actual
 '
@@ -387,7 +398,7 @@ test_expect_success AUTOIDENT 'message shows committer when it is automatic' '
 '
 
 write_script .git/FAKE_EDITOR <<EOF
-echo editor started > "$(pwd)/.git/result"
+echo editor started >"$(pwd)/.git/result"
 exit 0
 EOF
 
@@ -455,7 +466,7 @@ EOF
 test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' '
        echo >>negative &&
        ! "$SHELL_PATH" -c '\''
-         echo kill -TERM $$ >> .git/FAKE_EDITOR
+         echo kill -TERM $$ >>.git/FAKE_EDITOR
          GIT_EDITOR=.git/FAKE_EDITOR
          export GIT_EDITOR
          exec git commit -a'\'' &&
@@ -471,7 +482,8 @@ test_expect_success 'Hand committing of a redundant merge removes dups' '
        test_must_fail git merge second master &&
        git checkout master g &&
        EDITOR=: git commit -a &&
-       git cat-file commit HEAD | sed -n -e "s/^parent //p" -e "/^$/q" >actual &&
+       git cat-file commit HEAD >raw &&
+       sed -n -e "s/^parent //p" -e "/^$/q" raw >actual &&
        test_cmp expect actual
 
 '
@@ -480,7 +492,8 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo
 
        git reset --hard &&
        git commit -s -m "hello: kitty" --allow-empty &&
-       git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+       git cat-file commit HEAD >raw &&
+       sed -e "1,/^$/d" raw >actual &&
        test_line_count = 3 actual
 
 '
index 458608cc1e7ca51e1a7e40017794b5f2c55d3db3..c1eb72555d0c9878aafe04b8b3f69392a0b32dcd 100755 (executable)
@@ -780,6 +780,24 @@ EOF
        test_i18ncmp expected actual
 '
 
+test_expect_success 'status when cherry-picking after committing conflict resolution' '
+       git reset --hard cherry_branch &&
+       test_when_finished "git cherry-pick --abort" &&
+       test_must_fail git cherry-pick cherry_branch_second one_cherry &&
+       echo end >main.txt &&
+       git commit -a &&
+       cat >expected <<EOF &&
+On branch cherry_branch
+Cherry-pick currently in progress.
+  (run "git cherry-pick --continue" to continue)
+  (use "git cherry-pick --abort" to cancel the cherry-pick operation)
+
+nothing to commit (use -u to show untracked files)
+EOF
+       git status --untracked-files=no >actual &&
+       test_i18ncmp expected actual
+'
+
 test_expect_success 'status showing detached at and from a tag' '
        test_commit atag tagging &&
        git checkout atag &&
@@ -857,6 +875,24 @@ EOF
        test_i18ncmp expected actual
 '
 
+test_expect_success 'status while reverting after committing conflict resolution' '
+       test_when_finished "git revert --abort" &&
+       git reset --hard new &&
+       test_must_fail git revert old new &&
+       echo reverted >to-revert.txt &&
+       git commit -a &&
+       cat >expected <<EOF &&
+On branch master
+Revert currently in progress.
+  (run "git revert --continue" to continue)
+  (use "git revert --abort" to cancel the revert operation)
+
+nothing to commit (use -u to show untracked files)
+EOF
+       git status --untracked-files=no >actual &&
+       test_i18ncmp expected actual
+'
+
 test_expect_success 'prepare for different number of commits rebased' '
        git reset --hard master &&
        git checkout -b several_commits &&
index 106148254d0ce067e8f5f842faac8fbf1ee90f15..7f9c68cbe75a688357e6c71486c3897861421681 100755 (executable)
@@ -233,20 +233,65 @@ test_expect_success 'merge --squash c3 with c7' '
        cat result.9z >file &&
        git commit --no-edit -a &&
 
-       {
-               cat <<-EOF
-               Squashed commit of the following:
+       cat >expect <<-EOF &&
+       Squashed commit of the following:
 
-               $(git show -s c7)
+       $(git show -s c7)
 
-               # Conflicts:
-               #       file
-               EOF
-       } >expect &&
-       git cat-file commit HEAD | sed -e '1,/^$/d' >actual &&
+       # Conflicts:
+       #       file
+       EOF
+       git cat-file commit HEAD >raw &&
+       sed -e '1,/^$/d' raw >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '
+       git config commit.cleanup scissors &&
+       git reset --hard c3 &&
+       test_must_fail git merge c7 &&
+       cat result.9z >file &&
+       git commit --no-edit -a &&
+
+       cat >expect <<-\EOF &&
+       Merge tag '"'"'c7'"'"'
+
+       # ------------------------ >8 ------------------------
+       # Do not modify or remove the line above.
+       # Everything below it will be ignored.
+       #
+       # Conflicts:
+       #       file
+       EOF
+       git cat-file commit HEAD >raw &&
+       sed -e '1,/^$/d' raw >actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '
+       git config commit.cleanup scissors &&
+       git reset --hard c3 &&
+       test_must_fail git merge --squash c7 &&
+       cat result.9z >file &&
+       git commit --no-edit -a &&
+
+       cat >expect <<-EOF &&
+       Squashed commit of the following:
+
+       $(git show -s c7)
+
+       # ------------------------ >8 ------------------------
+       # Do not modify or remove the line above.
+       # Everything below it will be ignored.
+       #
+       # Conflicts:
+       #       file
+       EOF
+       git cat-file commit HEAD >raw &&
+       sed -e '1,/^$/d' raw >actual &&
+       test_i18ncmp expect actual
+'
+
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 and c3' '
@@ -680,10 +725,10 @@ cat >editor <<\EOF
 (
        echo "Merge work done on the side branch c1"
        echo
-       cat <"$1"
+       cat "$1"
 ) >"$1.tmp" && mv "$1.tmp" "$1"
 # strip comments and blank lines from end of message
-sed -e '/^#/d' < "$1" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' > expected
+sed -e '/^#/d' "$1" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' >expected
 EOF
 chmod 755 editor
 
@@ -768,14 +813,14 @@ test_expect_success 'set up mod-256 conflict scenario' '
        git commit -m base &&
 
        # one side changes the first line of each to "master"
-       sed s/-1/-master/ <file >tmp &&
+       sed s/-1/-master/ file >tmp &&
        mv tmp file &&
        git commit -am master &&
 
        # and the other to "side"; merging the two will
        # yield 256 separate conflicts
        git checkout -b side HEAD^ &&
-       sed s/-1/-side/ <file >tmp &&
+       sed s/-1/-side/ file >tmp &&
        mv tmp file &&
        git commit -am side
 '
@@ -814,7 +859,7 @@ EOF
 test_expect_success EXECKEEPSPID 'killed merge can be completed with --continue' '
        git reset --hard c0 &&
        ! "$SHELL_PATH" -c '\''
-         echo kill -TERM $$ >> .git/FAKE_EDITOR
+         echo kill -TERM $$ >>.git/FAKE_EDITOR
          GIT_EDITOR=.git/FAKE_EDITOR
          export GIT_EDITOR
          exec git merge --no-ff --edit c1'\'' &&
index 89619cf44608282efa6d75c2ed93cd3bdc4096fc..cd4f9607dc13ce7acfebc62d67bb4dbf1ef46135 100755 (executable)
@@ -16,16 +16,16 @@ create_merge_msgs() {
 }
 
 test_expect_success 'setup' '
-       echo c0 > c0.c &&
+       echo c0 >c0.c &&
        git add c0.c &&
        git commit -m c0 &&
        git tag c0 &&
-       echo c1 > c1.c &&
+       echo c1 >c1.c &&
        git add c1.c &&
        git commit -m c1 &&
        git tag c1 &&
        git reset --hard c0 &&
-       echo c2 > c2.c &&
+       echo c2 >c2.c &&
        git add c2.c &&
        git commit -m c2 &&
        git tag c2 &&
@@ -36,15 +36,80 @@ test_expect_success 'setup' '
 test_expect_success 'merge c2 with a custom message' '
        git reset --hard c1 &&
        git merge -m "$(cat exp.subject)" c2 &&
-       git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+       git cat-file commit HEAD >raw &&
+       sed -e "1,/^$/d" raw >actual &&
        test_cmp exp.subject actual
 '
 
 test_expect_success 'merge --log appends to custom message' '
        git reset --hard c1 &&
        git merge --log -m "$(cat exp.subject)" c2 &&
-       git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+       git cat-file commit HEAD >raw &&
+       sed -e "1,/^$/d" raw >actual &&
        test_cmp exp.log actual
 '
 
+mesg_with_comment_and_newlines='
+# text
+
+'
+
+test_expect_success 'prepare file with comment line and trailing newlines'  '
+       printf "%s" "$mesg_with_comment_and_newlines" >expect
+'
+
+test_expect_success 'cleanup commit messages (verbatim option)' '
+       git reset --hard c1 &&
+       git merge --cleanup=verbatim -F expect c2 &&
+       git cat-file commit HEAD >raw &&
+       sed -e "1,/^$/d" raw >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cleanup commit messages (whitespace option)' '
+       git reset --hard c1 &&
+       test_write_lines "" "# text" "" >text &&
+       echo "# text" >expect &&
+       git merge --cleanup=whitespace -F text c2 &&
+       git cat-file commit HEAD >raw &&
+       sed -e "1,/^$/d" raw >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cleanup merge messages (scissors option)' '
+       git reset --hard c1 &&
+       cat >text <<-\EOF &&
+
+       # to be kept
+
+         # ------------------------ >8 ------------------------
+       # to be kept, too
+       # ------------------------ >8 ------------------------
+       to be removed
+       # ------------------------ >8 ------------------------
+       to be removed, too
+       EOF
+
+       cat >expect <<-\EOF &&
+       # to be kept
+
+         # ------------------------ >8 ------------------------
+       # to be kept, too
+       EOF
+       git merge --cleanup=scissors -e -F text c2 &&
+       git cat-file commit HEAD >raw &&
+       sed -e "1,/^$/d" raw >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cleanup commit messages (strip option)' '
+       git reset --hard c1 &&
+       test_write_lines "" "# text" "sample" "" >text &&
+       echo sample >expect &&
+       git merge --cleanup=strip -F text c2 &&
+       git cat-file commit HEAD >raw &&
+       sed -e "1,/^$/d" raw >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 6162e2a8e66f6f0e42e0a8ba6ee3b728e8e84918..86d05160a3589cefd7ad21dbf64c655658f26342 100755 (executable)
@@ -221,5 +221,22 @@ test_expect_success 'repack --keep-pack' '
        )
 '
 
-test_done
+test_expect_success 'bitmaps are created by default in bare repos' '
+       git clone --bare .git bare.git &&
+       git -C bare.git repack -ad &&
+       bitmap=$(ls bare.git/objects/pack/*.bitmap) &&
+       test_path_is_file "$bitmap"
+'
+
+test_expect_success 'incremental repack does not complain' '
+       git -C bare.git repack -q 2>repack.err &&
+       test_must_be_empty repack.err
+'
 
+test_expect_success 'bitmaps can be disabled on bare repos' '
+       git -c repack.writeBitmaps=false -C bare.git repack -ad &&
+       bitmap=$(ls bare.git/objects/pack/*.bitmap 2>/dev/null || :) &&
+       test -z "$bitmap"
+'
+
+test_done
index fa475d52fa32bc257a215573943c84df6532bc8d..134a694516c924c5930b9c227da45e896297a338 100755 (executable)
@@ -380,11 +380,7 @@ test_expect_success 'grep --recurse-submodules should pass the pattern type alon
        fi
 '
 
-# Recursing down into nested submodules which do not have .gitmodules in their
-# working tree does not work yet. This is because config_from_gitmodules()
-# uses get_oid() and the latter is still not able to get objects from an
-# arbitrary repository (the nested submodule, in this case).
-test_expect_failure 'grep --recurse-submodules with submodules without .gitmodules in the working tree' '
+test_expect_success 'grep --recurse-submodules with submodules without .gitmodules in the working tree' '
        test_when_finished "git -C submodule checkout .gitmodules" &&
        rm submodule/.gitmodules &&
        git grep --recurse-submodules -e "(.|.)[\d]" >actual &&
index 788ea1f18b99c5edacd9ae0ee81310141c799c6e..8270de74beafb931f09f296557406c0d158d48de 100644 (file)
@@ -1239,6 +1239,12 @@ depacketize () {
        '
 }
 
+# Converts base-16 data into base-8. The output is given as a sequence of
+# escaped octals, suitable for consumption by 'printf'.
+hex2oct () {
+       perl -ne 'printf "\\%03o", hex for /../g'
+}
+
 # Set the hash algorithm in use to $1.  Only useful when testing the testsuite.
 test_set_hash () {
        test_hash_algo="$1"
diff --git a/tag.h b/tag.h
index e669c3e497a95bdeb08c4b45dec37a7d80b52ac4..03265fbfe2942f9ca7409cb2b771b14edd9fb998 100644 (file)
--- a/tag.h
+++ b/tag.h
@@ -11,13 +11,13 @@ struct tag {
        char *tag;
        timestamp_t date;
 };
-extern struct tag *lookup_tag(struct repository *r, const struct object_id *oid);
-extern int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, unsigned long size);
-extern int parse_tag(struct tag *item);
-extern void release_tag_memory(struct tag *t);
-extern struct object *deref_tag(struct repository *r, struct object *, const char *, int);
-extern struct object *deref_tag_noverify(struct object *);
-extern int gpg_verify_tag(const struct object_id *oid,
-               const char *name_to_report, unsigned flags);
+struct tag *lookup_tag(struct repository *r, const struct object_id *oid);
+int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, unsigned long size);
+int parse_tag(struct tag *item);
+void release_tag_memory(struct tag *t);
+struct object *deref_tag(struct repository *r, struct object *, const char *, int);
+struct object *deref_tag_noverify(struct object *);
+int gpg_verify_tag(const struct object_id *oid,
+                  const char *name_to_report, unsigned flags);
 
 #endif /* TAG_H */
index 61d8dc4d1bb2fc446ac7b452661064e615b4b5c9..cddda0a33c3e1ef39c6d294221fc9f7ae5205217 100644 (file)
@@ -89,7 +89,7 @@ struct tempfile {
  * a tempfile (whose "fd" member can be used for writing to it), or
  * NULL on error. It is an error if a file already exists at that path.
  */
-extern struct tempfile *create_tempfile(const char *path);
+struct tempfile *create_tempfile(const char *path);
 
 /*
  * Register an existing file as a tempfile, meaning that it will be
@@ -97,7 +97,7 @@ extern struct tempfile *create_tempfile(const char *path);
  * but it can be worked with like any other closed tempfile (for
  * example, it can be opened using reopen_tempfile()).
  */
-extern struct tempfile *register_tempfile(const char *path);
+struct tempfile *register_tempfile(const char *path);
 
 
 /*
@@ -136,8 +136,8 @@ extern struct tempfile *register_tempfile(const char *path);
  */
 
 /* See "mks_tempfile functions" above. */
-extern struct tempfile *mks_tempfile_sm(const char *filename_template,
-                                       int suffixlen, int mode);
+struct tempfile *mks_tempfile_sm(const char *filename_template,
+                                int suffixlen, int mode);
 
 /* See "mks_tempfile functions" above. */
 static inline struct tempfile *mks_tempfile_s(const char *filename_template,
@@ -159,8 +159,8 @@ static inline struct tempfile *mks_tempfile(const char *filename_template)
 }
 
 /* See "mks_tempfile functions" above. */
-extern struct tempfile *mks_tempfile_tsm(const char *filename_template,
-                                        int suffixlen, int mode);
+struct tempfile *mks_tempfile_tsm(const char *filename_template,
+                                 int suffixlen, int mode);
 
 /* See "mks_tempfile functions" above. */
 static inline struct tempfile *mks_tempfile_ts(const char *filename_template,
@@ -182,7 +182,7 @@ static inline struct tempfile *mks_tempfile_t(const char *filename_template)
 }
 
 /* See "mks_tempfile functions" above. */
-extern struct tempfile *xmks_tempfile_m(const char *filename_template, int mode);
+struct tempfile *xmks_tempfile_m(const char *filename_template, int mode);
 
 /* See "mks_tempfile functions" above. */
 static inline struct tempfile *xmks_tempfile(const char *filename_template)
@@ -196,7 +196,7 @@ static inline struct tempfile *xmks_tempfile(const char *filename_template)
  * stream is closed automatically when `close_tempfile_gently()` is called or
  * when the file is deleted or renamed.
  */
-extern FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode);
+FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode);
 
 static inline int is_tempfile_active(struct tempfile *tempfile)
 {
@@ -207,10 +207,10 @@ static inline int is_tempfile_active(struct tempfile *tempfile)
  * Return the path of the lockfile. The return value is a pointer to a
  * field within the lock_file object and should not be freed.
  */
-extern const char *get_tempfile_path(struct tempfile *tempfile);
+const char *get_tempfile_path(struct tempfile *tempfile);
 
-extern int get_tempfile_fd(struct tempfile *tempfile);
-extern FILE *get_tempfile_fp(struct tempfile *tempfile);
+int get_tempfile_fd(struct tempfile *tempfile);
+FILE *get_tempfile_fp(struct tempfile *tempfile);
 
 /*
  * If the temporary file is still open, close it (and the file pointer
@@ -220,7 +220,7 @@ extern FILE *get_tempfile_fp(struct tempfile *tempfile);
  * should eventually be called regardless of whether `close_tempfile_gently()`
  * succeeds.
  */
-extern int close_tempfile_gently(struct tempfile *tempfile);
+int close_tempfile_gently(struct tempfile *tempfile);
 
 /*
  * Re-open a temporary file that has been closed using
@@ -241,7 +241,7 @@ extern int close_tempfile_gently(struct tempfile *tempfile);
  *
  * * `rename_tempfile()` to move the file to its permanent location.
  */
-extern int reopen_tempfile(struct tempfile *tempfile);
+int reopen_tempfile(struct tempfile *tempfile);
 
 /*
  * Close the file descriptor and/or file pointer and remove the
@@ -249,7 +249,7 @@ extern int reopen_tempfile(struct tempfile *tempfile);
  * `delete_tempfile()` for a `tempfile` object that has already been
  * deleted or renamed.
  */
-extern void delete_tempfile(struct tempfile **tempfile_p);
+void delete_tempfile(struct tempfile **tempfile_p);
 
 /*
  * Close the file descriptor and/or file pointer if they are still
@@ -260,6 +260,6 @@ extern void delete_tempfile(struct tempfile **tempfile_p);
  * `rename(2)`. It is a bug to call `rename_tempfile()` for a
  * `tempfile` object that is not currently active.
  */
-extern int rename_tempfile(struct tempfile **tempfile_p, const char *path);
+int rename_tempfile(struct tempfile **tempfile_p, const char *path);
 
 #endif /* TEMPFILE_H */
diff --git a/trace.h b/trace.h
index 171b256d261c771927541cf4d431b926b3ba102b..9fa3e7a59407541d288da6ce2720279e2e23ab4e 100644 (file)
--- a/trace.h
+++ b/trace.h
@@ -17,34 +17,34 @@ extern struct trace_key trace_default_key;
 extern struct trace_key trace_perf_key;
 extern struct trace_key trace_setup_key;
 
-extern void trace_repo_setup(const char *prefix);
-extern int trace_want(struct trace_key *key);
-extern void trace_disable(struct trace_key *key);
-extern uint64_t getnanotime(void);
-extern void trace_command_performance(const char **argv);
-extern void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);
+void trace_repo_setup(const char *prefix);
+int trace_want(struct trace_key *key);
+void trace_disable(struct trace_key *key);
+uint64_t getnanotime(void);
+void trace_command_performance(const char **argv);
+void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);
 uint64_t trace_performance_enter(void);
 
 #ifndef HAVE_VARIADIC_MACROS
 
 __attribute__((format (printf, 1, 2)))
-extern void trace_printf(const char *format, ...);
+void trace_printf(const char *format, ...);
 
 __attribute__((format (printf, 2, 3)))
-extern void trace_printf_key(struct trace_key *key, const char *format, ...);
+void trace_printf_key(struct trace_key *key, const char *format, ...);
 
 __attribute__((format (printf, 2, 3)))
-extern void trace_argv_printf(const char **argv, const char *format, ...);
+void trace_argv_printf(const char **argv, const char *format, ...);
 
-extern void trace_strbuf(struct trace_key *key, const struct strbuf *data);
+void trace_strbuf(struct trace_key *key, const struct strbuf *data);
 
 /* Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled. */
 __attribute__((format (printf, 2, 3)))
-extern void trace_performance(uint64_t nanos, const char *format, ...);
+void trace_performance(uint64_t nanos, const char *format, ...);
 
 /* Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled. */
 __attribute__((format (printf, 2, 3)))
-extern void trace_performance_since(uint64_t start, const char *format, ...);
+void trace_performance_since(uint64_t start, const char *format, ...);
 
 __attribute__((format (printf, 1, 2)))
 void trace_performance_leave(const char *format, ...);
@@ -132,19 +132,19 @@ void trace_performance_leave(const char *format, ...);
 
 /* backend functions, use non-*fl macros instead */
 __attribute__((format (printf, 4, 5)))
-extern void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
-                               const char *format, ...);
+void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
+                        const char *format, ...);
 __attribute__((format (printf, 4, 5)))
-extern void trace_argv_printf_fl(const char *file, int line, const char **argv,
-                                const char *format, ...);
-extern void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
-                           const struct strbuf *data);
+void trace_argv_printf_fl(const char *file, int line, const char **argv,
+                         const char *format, ...);
+void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
+                    const struct strbuf *data);
 __attribute__((format (printf, 4, 5)))
-extern void trace_performance_fl(const char *file, int line,
-                                uint64_t nanos, const char *fmt, ...);
+void trace_performance_fl(const char *file, int line,
+                         uint64_t nanos, const char *fmt, ...);
 __attribute__((format (printf, 4, 5)))
-extern void trace_performance_leave_fl(const char *file, int line,
-                                      uint64_t nanos, const char *fmt, ...);
+void trace_performance_leave_fl(const char *file, int line,
+                               uint64_t nanos, const char *fmt, ...);
 static inline int trace_pass_fl(struct trace_key *key)
 {
        return key->fd || !key->initialized;
index 8bbad568871dc2a8526d47ae4cb9f36f49fcb7a3..c7b4f14d29a9d0ef38fba7595d15911757b9cbdb 100644 (file)
--- a/trace2.c
+++ b/trace2.c
@@ -10,6 +10,7 @@
 #include "trace2/tr2_cmd_name.h"
 #include "trace2/tr2_dst.h"
 #include "trace2/tr2_sid.h"
+#include "trace2/tr2_sysenv.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 
@@ -120,6 +121,7 @@ static void tr2main_atexit_handler(void)
        tr2_sid_release();
        tr2_cmd_name_release();
        tr2_cfg_free_patterns();
+       tr2_sysenv_release();
 
        trace2_enabled = 0;
 }
@@ -142,6 +144,11 @@ static void tr2main_signal_handler(int signo)
        raise(signo);
 }
 
+void trace2_initialize_clock(void)
+{
+       tr2tls_start_process_clock();
+}
+
 void trace2_initialize_fl(const char *file, int line)
 {
        struct tr2_tgt *tgt_j;
@@ -150,6 +157,8 @@ void trace2_initialize_fl(const char *file, int line)
        if (trace2_enabled)
                return;
 
+       tr2_sysenv_load();
+
        if (!tr2_tgt_want_builtins())
                return;
        trace2_enabled = 1;
@@ -177,13 +186,19 @@ void trace2_cmd_start_fl(const char *file, int line, const char **argv)
 {
        struct tr2_tgt *tgt_j;
        int j;
+       uint64_t us_now;
+       uint64_t us_elapsed_absolute;
 
        if (!trace2_enabled)
                return;
 
+       us_now = getnanotime() / 1000;
+       us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_start_fl)
-                       tgt_j->pfn_start_fl(file, line, argv);
+                       tgt_j->pfn_start_fl(file, line, us_elapsed_absolute,
+                                           argv);
 }
 
 int trace2_cmd_exit_fl(const char *file, int line, int code)
@@ -198,6 +213,8 @@ int trace2_cmd_exit_fl(const char *file, int line, int code)
        if (!trace2_enabled)
                return code;
 
+       trace2_collect_process_info(TRACE2_PROCESS_INFO_EXIT);
+
        tr2main_exit_code = code;
 
        us_now = getnanotime() / 1000;
@@ -428,7 +445,7 @@ void trace2_thread_start_fl(const char *file, int line, const char *thread_name)
        us_now = getnanotime() / 1000;
        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 
-       tr2tls_create_self(thread_name);
+       tr2tls_create_self(thread_name, us_now);
 
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_thread_start_fl)
@@ -717,7 +734,7 @@ void trace2_data_json_fl(const char *file, int line, const char *category,
        us_elapsed_region = tr2tls_region_elasped_self(us_now);
 
        for_each_wanted_builtin (j, tgt_j)
-               if (tgt_j->pfn_data_fl)
+               if (tgt_j->pfn_data_json_fl)
                        tgt_j->pfn_data_json_fl(file, line, us_elapsed_absolute,
                                                us_elapsed_region, category,
                                                repo, key, value);
index b330a54a89a08c2e1f764db6708a8f55a7479f3b..f189ef5984f0e2e699a1a2a7a0e8fd93e5e0281c 100644 (file)
--- a/trace2.h
+++ b/trace2.h
@@ -19,9 +19,27 @@ struct json_writer;
  * [] trace2_printf*    -- legacy trace[1] messages.
  */
 
+/*
+ * Initialize the TRACE2 clock and do nothing else, in particular
+ * no mallocs, no system inspection, and no environment inspection.
+ *
+ * This should be called at the very top of main() to capture the
+ * process start time.  This is intended to reduce chicken-n-egg
+ * bootstrap pressure.
+ *
+ * It is safe to call this more than once.  This allows capturing
+ * absolute startup costs on Windows which uses a little trickery
+ * to do setup work before common-main.c:main() is called.
+ *
+ * The main trace2_initialize_fl() may be called a little later
+ * after more infrastructure is established.
+ */
+void trace2_initialize_clock(void);
+
 /*
  * Initialize TRACE2 tracing facility if any of the builtin TRACE2
- * targets are enabled in the environment.  Emits a 'version' event.
+ * targets are enabled in the system config or the environment.
+ * Emits a 'version' event.
  *
  * Cleanup/Termination is handled automatically by a registered
  * atexit() routine.
@@ -108,10 +126,11 @@ void trace2_cmd_alias_fl(const char *file, int line, const char *alias,
  * Emit one or more 'def_param' events for "interesting" configuration
  * settings.
  *
- * The environment variable "GIT_TR2_CONFIG_PARAMS" can be set to a
- * list of patterns considered important.  For example:
- *
- *    GIT_TR2_CONFIG_PARAMS="core.*,remote.*.url"
+ * Use the TR2_SYSENV_CFG_PARAM setting to register a comma-separated
+ * list of patterns configured important.  For example:
+ *     git config --system trace2.configParams 'core.*,remote.*.url'
+ * or:
+ *     GIT_TR2_CONFIG_PARAMS=core.*,remote.*.url"
  *
  * Note: this routine does a read-only iteration on the config data
  * (using read_early_config()), so it must not be called until enough
@@ -372,13 +391,19 @@ void trace2_printf(const char *fmt, ...);
  * Optional platform-specific code to dump information about the
  * current and any parent process(es).  This is intended to allow
  * post-processors to know who spawned this git instance and anything
- * else the platform may be able to tell us about the current process.
+ * else that the platform may be able to tell us about the current process.
  */
+
+enum trace2_process_info_reason {
+       TRACE2_PROCESS_INFO_STARTUP,
+       TRACE2_PROCESS_INFO_EXIT,
+};
+
 #if defined(GIT_WINDOWS_NATIVE)
-void trace2_collect_process_info(void);
+void trace2_collect_process_info(enum trace2_process_info_reason reason);
 #else
-#define trace2_collect_process_info() \
-       do {                          \
+#define trace2_collect_process_info(reason) \
+       do {                                \
        } while (0)
 #endif
 
index b329921ac5a583567485f03d69dbfa70ab8d680a..caa7f06948ab97c09ee9adf82a9588fcc07d6e52 100644 (file)
@@ -1,8 +1,7 @@
 #include "cache.h"
 #include "config.h"
-#include "tr2_cfg.h"
-
-#define TR2_ENVVAR_CFG_PARAM "GIT_TR2_CONFIG_PARAMS"
+#include "trace2/tr2_cfg.h"
+#include "trace2/tr2_sysenv.h"
 
 static struct strbuf **tr2_cfg_patterns;
 static int tr2_cfg_count_patterns;
@@ -21,7 +20,7 @@ static int tr2_cfg_load_patterns(void)
                return tr2_cfg_count_patterns;
        tr2_cfg_loaded = 1;
 
-       envvar = getenv(TR2_ENVVAR_CFG_PARAM);
+       envvar = tr2_sysenv_get(TR2_SYSENV_CFG_PARAM);
        if (!envvar || !*envvar)
                return tr2_cfg_count_patterns;
 
index fd490a43ad923d2c40c7f0139e71a3d2259f8075..5dda0ca1cdb5d09a8cdfb27dd45d4436f3e8aa6f 100644 (file)
@@ -1,23 +1,19 @@
 #include "cache.h"
 #include "trace2/tr2_dst.h"
+#include "trace2/tr2_sid.h"
+#include "trace2/tr2_sysenv.h"
 
 /*
- * If a Trace2 target cannot be opened for writing, we should issue a
- * warning to stderr, but this is very annoying if the target is a pipe
- * or socket and beyond the user's control -- especially since every
- * git command (and sub-command) will print the message.  So we silently
- * eat these warnings and just discard the trace data.
- *
- * Enable the following environment variable to see these warnings.
+ * How many attempts we will make at creating an automatically-named trace file.
  */
-#define TR2_ENVVAR_DST_DEBUG "GIT_TR2_DST_DEBUG"
+#define MAX_AUTO_ATTEMPTS 10
 
 static int tr2_dst_want_warning(void)
 {
        static int tr2env_dst_debug = -1;
 
        if (tr2env_dst_debug == -1) {
-               const char *env_value = getenv(TR2_ENVVAR_DST_DEBUG);
+               const char *env_value = tr2_sysenv_get(TR2_SYSENV_DST_DEBUG);
                if (!env_value || !*env_value)
                        tr2env_dst_debug = 0;
                else
@@ -36,13 +32,65 @@ void tr2_dst_trace_disable(struct tr2_dst *dst)
        dst->need_close = 0;
 }
 
+static int tr2_dst_try_auto_path(struct tr2_dst *dst, const char *tgt_prefix)
+{
+       int fd;
+       const char *last_slash, *sid = tr2_sid_get();
+       struct strbuf path = STRBUF_INIT;
+       size_t base_path_len;
+       unsigned attempt_count;
+
+       last_slash = strrchr(sid, '/');
+       if (last_slash)
+               sid = last_slash + 1;
+
+       strbuf_addstr(&path, tgt_prefix);
+       if (!is_dir_sep(path.buf[path.len - 1]))
+               strbuf_addch(&path, '/');
+       strbuf_addstr(&path, sid);
+       base_path_len = path.len;
+
+       for (attempt_count = 0; attempt_count < MAX_AUTO_ATTEMPTS; attempt_count++) {
+               if (attempt_count > 0) {
+                       strbuf_setlen(&path, base_path_len);
+                       strbuf_addf(&path, ".%d", attempt_count);
+               }
+
+               fd = open(path.buf, O_WRONLY | O_CREAT | O_EXCL, 0666);
+               if (fd != -1)
+                       break;
+       }
+
+       if (fd == -1) {
+               if (tr2_dst_want_warning())
+                       warning("trace2: could not open '%.*s' for '%s' tracing: %s",
+                               (int) base_path_len, path.buf,
+                               tr2_sysenv_display_name(dst->sysenv_var),
+                               strerror(errno));
+
+               tr2_dst_trace_disable(dst);
+               strbuf_release(&path);
+               return 0;
+       }
+
+       strbuf_release(&path);
+
+       dst->fd = fd;
+       dst->need_close = 1;
+       dst->initialized = 1;
+
+       return dst->fd;
+}
+
 static int tr2_dst_try_path(struct tr2_dst *dst, const char *tgt_value)
 {
        int fd = open(tgt_value, O_WRONLY | O_APPEND | O_CREAT, 0666);
        if (fd == -1) {
                if (tr2_dst_want_warning())
                        warning("trace2: could not open '%s' for '%s' tracing: %s",
-                               tgt_value, dst->env_var_name, strerror(errno));
+                               tgt_value,
+                               tr2_sysenv_display_name(dst->sysenv_var),
+                               strerror(errno));
 
                tr2_dst_trace_disable(dst);
                return 0;
@@ -116,7 +164,8 @@ static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst,
        if (!path || !*path) {
                if (tr2_dst_want_warning())
                        warning("trace2: invalid AF_UNIX value '%s' for '%s' tracing",
-                               tgt_value, dst->env_var_name);
+                               tgt_value,
+                               tr2_sysenv_display_name(dst->sysenv_var));
 
                tr2_dst_trace_disable(dst);
                return 0;
@@ -126,7 +175,7 @@ static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst,
            strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path)) {
                if (tr2_dst_want_warning())
                        warning("trace2: invalid AF_UNIX path '%s' for '%s' tracing",
-                               path, dst->env_var_name);
+                               path, tr2_sysenv_display_name(dst->sysenv_var));
 
                tr2_dst_trace_disable(dst);
                return 0;
@@ -148,7 +197,8 @@ static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst,
 error:
        if (tr2_dst_want_warning())
                warning("trace2: could not connect to socket '%s' for '%s' tracing: %s",
-                       path, dst->env_var_name, strerror(e));
+                       path, tr2_sysenv_display_name(dst->sysenv_var),
+                       strerror(e));
 
        tr2_dst_trace_disable(dst);
        return 0;
@@ -168,7 +218,7 @@ static void tr2_dst_malformed_warning(struct tr2_dst *dst,
        struct strbuf buf = STRBUF_INIT;
 
        strbuf_addf(&buf, "trace2: unknown value for '%s': '%s'",
-                   dst->env_var_name, tgt_value);
+                   tr2_sysenv_display_name(dst->sysenv_var), tgt_value);
        warning("%s", buf.buf);
 
        strbuf_release(&buf);
@@ -184,7 +234,7 @@ int tr2_dst_get_trace_fd(struct tr2_dst *dst)
 
        dst->initialized = 1;
 
-       tgt_value = getenv(dst->env_var_name);
+       tgt_value = tr2_sysenv_get(dst->sysenv_var);
 
        if (!tgt_value || !strcmp(tgt_value, "") || !strcmp(tgt_value, "0") ||
            !strcasecmp(tgt_value, "false")) {
@@ -202,8 +252,12 @@ int tr2_dst_get_trace_fd(struct tr2_dst *dst)
                return dst->fd;
        }
 
-       if (is_absolute_path(tgt_value))
-               return tr2_dst_try_path(dst, tgt_value);
+       if (is_absolute_path(tgt_value)) {
+               if (is_directory(tgt_value))
+                       return tr2_dst_try_auto_path(dst, tgt_value);
+               else
+                       return tr2_dst_try_path(dst, tgt_value);
+       }
 
 #ifndef NO_UNIX_SOCKETS
        if (starts_with(tgt_value, PREFIX_AF_UNIX))
@@ -246,7 +300,8 @@ void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line)
                return;
 
        if (tr2_dst_want_warning())
-               warning("unable to write trace to '%s': %s", dst->env_var_name,
+               warning("unable to write trace to '%s': %s",
+                       tr2_sysenv_display_name(dst->sysenv_var),
                        strerror(errno));
        tr2_dst_trace_disable(dst);
 }
index 9a64f05b0299df2444f7a1d93ccac77a713e40c2..3adf3bac139b1aa910b4aea0dc71e3b03ce722ed 100644 (file)
@@ -2,9 +2,10 @@
 #define TR2_DST_H
 
 struct strbuf;
+#include "trace2/tr2_sysenv.h"
 
 struct tr2_dst {
-       const char *const env_var_name;
+       enum tr2_sysenv_variable sysenv_var;
        int fd;
        unsigned int initialized : 1;
        unsigned int need_close : 1;
index 984524a43ce44536e1dc825995ee4e0aa80734fb..50470954783672d7642d3359c5c581ace5f7b86c 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_sid.h"
 
 #define TR2_ENVVAR_PARENT_SID "GIT_TR2_PARENT_SID"
@@ -6,6 +7,53 @@
 static struct strbuf tr2sid_buf = STRBUF_INIT;
 static int tr2sid_nr_git_parents;
 
+/*
+ * Compute the final component of the SID representing the current process.
+ * This should uniquely identify the process and be a valid filename (to
+ * allow writing trace2 data to per-process files).  It should also be fixed
+ * length for possible use as a database key.
+ *
+ * "<yyyymmdd>T<hhmmss>.<fraction>Z-<host>-<process>"
+ *
+ * where <host> is a 9 character string:
+ *    "H<first_8_chars_of_sha1_of_hostname>"
+ *    "Localhost" when no hostname.
+ *
+ * where <process> is a 9 character string containing the least signifcant
+ * 32 bits in the process-id.
+ *    "P<pid>"
+ * (This is an abribrary choice.  On most systems pid_t is a 32 bit value,
+ * so limit doesn't matter.  On larger systems, a truncated value is fine
+ * for our purposes here.)
+ */
+static void tr2_sid_append_my_sid_component(void)
+{
+       const struct git_hash_algo *algo = &hash_algos[GIT_HASH_SHA1];
+       struct tr2_tbuf tb_now;
+       git_hash_ctx ctx;
+       pid_t pid = getpid();
+       unsigned char hash[GIT_MAX_RAWSZ + 1];
+       char hex[GIT_MAX_HEXSZ + 1];
+       char hostname[HOST_NAME_MAX + 1];
+
+       tr2_tbuf_utc_datetime(&tb_now);
+       strbuf_addstr(&tr2sid_buf, tb_now.buf);
+
+       strbuf_addch(&tr2sid_buf, '-');
+       if (xgethostname(hostname, sizeof(hostname)))
+               strbuf_add(&tr2sid_buf, "Localhost", 9);
+       else {
+               algo->init_fn(&ctx);
+               algo->update_fn(&ctx, hostname, strlen(hostname));
+               algo->final_fn(hash, &ctx);
+               hash_to_hex_algop_r(hex, hash, algo);
+               strbuf_addch(&tr2sid_buf, 'H');
+               strbuf_add(&tr2sid_buf, hex, 8);
+       }
+
+       strbuf_addf(&tr2sid_buf, "-P%08"PRIx32, (uint32_t)pid);
+}
+
 /*
  * Compute a "unique" session id (SID) for the current process.  This allows
  * all events from this process to have a single label (much like a PID).
@@ -20,7 +68,6 @@ static int tr2sid_nr_git_parents;
  */
 static void tr2_sid_compute(void)
 {
-       uint64_t us_now;
        const char *parent_sid;
 
        if (tr2sid_buf.len)
@@ -38,9 +85,7 @@ static void tr2_sid_compute(void)
                tr2sid_nr_git_parents++;
        }
 
-       us_now = getnanotime() / 1000;
-       strbuf_addf(&tr2sid_buf, "%" PRIuMAX "-%" PRIdMAX, (uintmax_t)us_now,
-                   (intmax_t)getpid());
+       tr2_sid_append_my_sid_component();
 
        setenv(TR2_ENVVAR_PARENT_SID, tr2sid_buf.buf, 1);
 }
diff --git a/trace2/tr2_sysenv.c b/trace2/tr2_sysenv.c
new file mode 100644 (file)
index 0000000..9025b86
--- /dev/null
@@ -0,0 +1,127 @@
+#include "cache.h"
+#include "config.h"
+#include "dir.h"
+#include "tr2_sysenv.h"
+
+/*
+ * Each entry represents a trace2 setting.
+ * See Documentation/technical/api-trace2.txt
+ */
+struct tr2_sysenv_entry {
+       const char *env_var_name;
+       const char *git_config_name;
+
+       char *value;
+       unsigned int getenv_called : 1;
+};
+
+/*
+ * This table must match "enum tr2_sysenv_variable" in tr2_sysenv.h.
+ *
+ * The strings in this table are constant and must match the published
+ * config and environment variable names as described in the documentation.
+ *
+ * We do not define entries for the GIT_TR2_PARENT_* environment
+ * variables because they are transient and used to pass information
+ * from parent to child git processes, rather than settings.
+ */
+/* clang-format off */
+static struct tr2_sysenv_entry tr2_sysenv_settings[] = {
+       [TR2_SYSENV_CFG_PARAM]     = { "GIT_TR2_CONFIG_PARAMS",
+                                      "trace2.configparams" },
+
+       [TR2_SYSENV_DST_DEBUG]     = { "GIT_TR2_DST_DEBUG",
+                                      "trace2.destinationdebug" },
+
+       [TR2_SYSENV_NORMAL]        = { "GIT_TR2",
+                                      "trace2.normaltarget" },
+       [TR2_SYSENV_NORMAL_BRIEF]  = { "GIT_TR2_BRIEF",
+                                      "trace2.normalbrief" },
+
+       [TR2_SYSENV_EVENT]         = { "GIT_TR2_EVENT",
+                                      "trace2.eventtarget" },
+       [TR2_SYSENV_EVENT_BRIEF]   = { "GIT_TR2_EVENT_BRIEF",
+                                      "trace2.eventbrief" },
+       [TR2_SYSENV_EVENT_NESTING] = { "GIT_TR2_EVENT_NESTING",
+                                      "trace2.eventnesting" },
+
+       [TR2_SYSENV_PERF]          = { "GIT_TR2_PERF",
+                                      "trace2.perftarget" },
+       [TR2_SYSENV_PERF_BRIEF]    = { "GIT_TR2_PERF_BRIEF",
+                                      "trace2.perfbrief" },
+};
+/* clang-format on */
+
+static int tr2_sysenv_cb(const char *key, const char *value, void *d)
+{
+       int k;
+
+       if (!starts_with(key, "trace2."))
+               return 0;
+
+       for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++) {
+               if (!strcmp(key, tr2_sysenv_settings[k].git_config_name)) {
+                       free(tr2_sysenv_settings[k].value);
+                       tr2_sysenv_settings[k].value = xstrdup(value);
+                       return 0;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * Load Trace2 settings from the system config (usually "/etc/gitconfig"
+ * unless we were built with a runtime-prefix).  These are intended to
+ * define the default values for Trace2 as requested by the administrator.
+ *
+ * Then override with the Trace2 settings from the global config.
+ */
+void tr2_sysenv_load(void)
+{
+       if (ARRAY_SIZE(tr2_sysenv_settings) != TR2_SYSENV_MUST_BE_LAST)
+               BUG("tr2_sysenv_settings size is wrong");
+
+       read_very_early_config(tr2_sysenv_cb, NULL);
+}
+
+/*
+ * Return the value for the requested Trace2 setting from these sources:
+ * the system config, the global config, and the environment.
+ */
+const char *tr2_sysenv_get(enum tr2_sysenv_variable var)
+{
+       if (var >= TR2_SYSENV_MUST_BE_LAST)
+               BUG("tr2_sysenv_get invalid var '%d'", var);
+
+       if (!tr2_sysenv_settings[var].getenv_called) {
+               const char *v = getenv(tr2_sysenv_settings[var].env_var_name);
+               if (v && *v) {
+                       free(tr2_sysenv_settings[var].value);
+                       tr2_sysenv_settings[var].value = xstrdup(v);
+               }
+               tr2_sysenv_settings[var].getenv_called = 1;
+       }
+
+       return tr2_sysenv_settings[var].value;
+}
+
+/*
+ * Return a friendly name for this setting that is suitable for printing
+ * in an error messages.
+ */
+const char *tr2_sysenv_display_name(enum tr2_sysenv_variable var)
+{
+       if (var >= TR2_SYSENV_MUST_BE_LAST)
+               BUG("tr2_sysenv_get invalid var '%d'", var);
+
+       return tr2_sysenv_settings[var].env_var_name;
+}
+
+void tr2_sysenv_release(void)
+{
+       int k;
+
+       for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++)
+               free(tr2_sysenv_settings[k].value);
+}
diff --git a/trace2/tr2_sysenv.h b/trace2/tr2_sysenv.h
new file mode 100644 (file)
index 0000000..369b20b
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef TR2_SYSENV_H
+#define TR2_SYSENV_H
+
+/*
+ * The Trace2 settings that can be loaded from /etc/gitconfig
+ * and/or user environment variables.
+ *
+ * Note that this set does not contain any of the transient
+ * environment variables used to pass information from parent
+ * to child git processes, such "GIT_TR2_PARENT_SID".
+ */
+enum tr2_sysenv_variable {
+       TR2_SYSENV_CFG_PARAM = 0,
+
+       TR2_SYSENV_DST_DEBUG,
+
+       TR2_SYSENV_NORMAL,
+       TR2_SYSENV_NORMAL_BRIEF,
+
+       TR2_SYSENV_EVENT,
+       TR2_SYSENV_EVENT_BRIEF,
+       TR2_SYSENV_EVENT_NESTING,
+
+       TR2_SYSENV_PERF,
+       TR2_SYSENV_PERF_BRIEF,
+
+       TR2_SYSENV_MUST_BE_LAST
+};
+
+void tr2_sysenv_load(void);
+
+const char *tr2_sysenv_get(enum tr2_sysenv_variable);
+const char *tr2_sysenv_display_name(enum tr2_sysenv_variable var);
+void tr2_sysenv_release(void);
+
+#endif /* TR2_SYSENV_H */
index 0844910423679374bcf418bf1abd79c8edddcbe7..2498482d9ad82a29b2e734b0d726599d594a20c6 100644 (file)
@@ -15,7 +15,7 @@ void tr2_tbuf_local_time(struct tr2_tbuf *tb)
                  tm.tm_min, tm.tm_sec, (long)tv.tv_usec);
 }
 
-void tr2_tbuf_utc_time(struct tr2_tbuf *tb)
+void tr2_tbuf_utc_datetime_extended(struct tr2_tbuf *tb)
 {
        struct timeval tv;
        struct tm tm;
@@ -26,7 +26,22 @@ void tr2_tbuf_utc_time(struct tr2_tbuf *tb)
        gmtime_r(&secs, &tm);
 
        xsnprintf(tb->buf, sizeof(tb->buf),
-                 "%4d-%02d-%02d %02d:%02d:%02d.%06ld", tm.tm_year + 1900,
+                 "%4d-%02d-%02dT%02d:%02d:%02d.%06ldZ", tm.tm_year + 1900,
                  tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
                  (long)tv.tv_usec);
 }
+
+void tr2_tbuf_utc_datetime(struct tr2_tbuf *tb)
+{
+       struct timeval tv;
+       struct tm tm;
+       time_t secs;
+
+       gettimeofday(&tv, NULL);
+       secs = tv.tv_sec;
+       gmtime_r(&secs, &tm);
+
+       xsnprintf(tb->buf, sizeof(tb->buf), "%4d%02d%02dT%02d%02d%02d.%06ldZ",
+                 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
+                 tm.tm_min, tm.tm_sec, (long)tv.tv_usec);
+}
index 9cdefa395749f02286c4f821d51bf78c6c06d181..fa853d8f4211b5c34ec592308cf028e3dbd0b406 100644 (file)
@@ -16,8 +16,9 @@ struct tr2_tbuf {
 void tr2_tbuf_local_time(struct tr2_tbuf *tb);
 
 /*
- * Fill buffer with formatted UTC time string.
+ * Fill buffer with formatted UTC datatime string.
  */
-void tr2_tbuf_utc_time(struct tr2_tbuf *tb);
+void tr2_tbuf_utc_datetime_extended(struct tr2_tbuf *tb);
+void tr2_tbuf_utc_datetime(struct tr2_tbuf *tb);
 
 #endif /* TR2_TBUF_H */
index 297bb8ffbe498358b41e697c892c259871a6a937..7b904692123e28074458fce0c0afe06922caa607 100644 (file)
@@ -15,6 +15,7 @@ typedef void(tr2_tgt_term_t)(void);
 typedef void(tr2_tgt_evt_version_fl_t)(const char *file, int line);
 
 typedef void(tr2_tgt_evt_start_fl_t)(const char *file, int line,
+                                    uint64_t us_elapsed_absolute,
                                     const char **argv);
 typedef void(tr2_tgt_evt_exit_fl_t)(const char *file, int line,
                                    uint64_t us_elapsed_absolute, int code);
index 1cf4f62441c9307d610ebb0274ddb272ec2b8f01..c2852d1bd2bd856d518b5ce499d38e7b13bb452c 100644 (file)
@@ -6,10 +6,11 @@
 #include "trace2/tr2_dst.h"
 #include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_sid.h"
+#include "trace2/tr2_sysenv.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 
-static struct tr2_dst tr2dst_event = { "GIT_TR2_EVENT", 0, 0, 0 };
+static struct tr2_dst tr2dst_event = { TR2_SYSENV_EVENT, 0, 0, 0 };
 
 /*
  * The version number of the JSON data generated by the EVENT target
@@ -28,37 +29,36 @@ static struct tr2_dst tr2dst_event = { "GIT_TR2_EVENT", 0, 0, 0 };
  * are primarily intended for the performance target during debugging.
  *
  * Some of the outer-most messages, however, may be of interest to the
- * event target.  Set this environment variable to a larger integer for
- * more detail in the event target.
+ * event target.  Use the TR2_SYSENV_EVENT_NESTING setting to increase
+ * region details in the event target.
  */
-#define TR2_ENVVAR_EVENT_NESTING "GIT_TR2_EVENT_NESTING"
-static int tr2env_event_nesting_wanted = 2;
+static int tr2env_event_max_nesting_levels = 2;
 
 /*
- * Set this environment variable to true to omit the <time>, <file>, and
+ * Use the TR2_SYSENV_EVENT_BRIEF to omit the <time>, <file>, and
  * <line> fields from most events.
  */
-#define TR2_ENVVAR_EVENT_BRIEF "GIT_TR2_EVENT_BRIEF"
-static int tr2env_event_brief;
+static int tr2env_event_be_brief;
 
 static int fn_init(void)
 {
        int want = tr2_dst_trace_want(&tr2dst_event);
-       int want_nesting;
+       int max_nesting;
        int want_brief;
-       char *nesting;
-       char *brief;
+       const char *nesting;
+       const char *brief;
 
        if (!want)
                return want;
 
-       nesting = getenv(TR2_ENVVAR_EVENT_NESTING);
-       if (nesting && ((want_nesting = atoi(nesting)) > 0))
-               tr2env_event_nesting_wanted = want_nesting;
+       nesting = tr2_sysenv_get(TR2_SYSENV_EVENT_NESTING);
+       if (nesting && *nesting && ((max_nesting = atoi(nesting)) > 0))
+               tr2env_event_max_nesting_levels = max_nesting;
 
-       brief = getenv(TR2_ENVVAR_EVENT_BRIEF);
-       if (brief && ((want_brief = atoi(brief)) > 0))
-               tr2env_event_brief = want_brief;
+       brief = tr2_sysenv_get(TR2_SYSENV_EVENT_BRIEF);
+       if (brief && *brief &&
+           ((want_brief = git_parse_maybe_bool(brief)) != -1))
+               tr2env_event_be_brief = want_brief;
 
        return want;
 }
@@ -92,13 +92,13 @@ static void event_fmt_prepare(const char *event_name, const char *file,
        /*
         * In brief mode, only emit <time> on these 2 event types.
         */
-       if (!tr2env_event_brief || !strcmp(event_name, "version") ||
+       if (!tr2env_event_be_brief || !strcmp(event_name, "version") ||
            !strcmp(event_name, "atexit")) {
-               tr2_tbuf_utc_time(&tb_now);
+               tr2_tbuf_utc_datetime_extended(&tb_now);
                jw_object_string(jw, "time", tb_now.buf);
        }
 
-       if (!tr2env_event_brief && file && *file) {
+       if (!tr2env_event_be_brief && file && *file) {
                jw_object_string(jw, "file", file);
                jw_object_intmax(jw, "line", line);
        }
@@ -122,13 +122,16 @@ static void fn_version_fl(const char *file, int line)
        jw_release(&jw);
 }
 
-static void fn_start_fl(const char *file, int line, const char **argv)
+static void fn_start_fl(const char *file, int line,
+                       uint64_t us_elapsed_absolute, const char **argv)
 {
        const char *event_name = "start";
        struct json_writer jw = JSON_WRITER_INIT;
+       double t_abs = (double)us_elapsed_absolute / 1000000.0;
 
        jw_object_begin(&jw, 0);
        event_fmt_prepare(event_name, file, line, NULL, &jw);
+       jw_object_double(&jw, "t_abs", 6, t_abs);
        jw_object_inline_begin_array(&jw, "argv");
        jw_array_argv(&jw, argv);
        jw_end(&jw);
@@ -456,7 +459,7 @@ static void fn_region_enter_printf_va_fl(const char *file, int line,
 {
        const char *event_name = "region_enter";
        struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
-       if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) {
+       if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) {
                struct json_writer jw = JSON_WRITER_INIT;
 
                jw_object_begin(&jw, 0);
@@ -481,7 +484,7 @@ static void fn_region_leave_printf_va_fl(
 {
        const char *event_name = "region_leave";
        struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
-       if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) {
+       if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) {
                struct json_writer jw = JSON_WRITER_INIT;
                double t_rel = (double)us_elapsed_region / 1000000.0;
 
@@ -508,7 +511,7 @@ static void fn_data_fl(const char *file, int line, uint64_t us_elapsed_absolute,
 {
        const char *event_name = "data";
        struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
-       if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) {
+       if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) {
                struct json_writer jw = JSON_WRITER_INIT;
                double t_abs = (double)us_elapsed_absolute / 1000000.0;
                double t_rel = (double)us_elapsed_region / 1000000.0;
@@ -536,7 +539,7 @@ static void fn_data_json_fl(const char *file, int line,
 {
        const char *event_name = "data_json";
        struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
-       if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) {
+       if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) {
                struct json_writer jw = JSON_WRITER_INIT;
                double t_abs = (double)us_elapsed_absolute / 1000000.0;
                double t_rel = (double)us_elapsed_region / 1000000.0;
index 1a07d70abd6de281fc2a865f026fbb2e426adf96..00b116d797c844cd7320542a9829e94ee1e0311c 100644 (file)
@@ -4,20 +4,20 @@
 #include "quote.h"
 #include "version.h"
 #include "trace2/tr2_dst.h"
+#include "trace2/tr2_sysenv.h"
 #include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 
-static struct tr2_dst tr2dst_normal = { "GIT_TR2", 0, 0, 0 };
+static struct tr2_dst tr2dst_normal = { TR2_SYSENV_NORMAL, 0, 0, 0 };
 
 /*
- * Set this environment variable to true to omit the "<time> <file>:<line>"
+ * Use the TR2_SYSENV_NORMAL_BRIEF setting to omit the "<time> <file>:<line>"
  * fields from each line written to the builtin normal target.
  *
  * Unit tests may want to use this to help with testing.
  */
-#define TR2_ENVVAR_NORMAL_BRIEF "GIT_TR2_BRIEF"
-static int tr2env_normal_brief;
+static int tr2env_normal_be_brief;
 
 #define TR2FMT_NORMAL_FL_WIDTH (50)
 
@@ -25,15 +25,15 @@ static int fn_init(void)
 {
        int want = tr2_dst_trace_want(&tr2dst_normal);
        int want_brief;
-       char *brief;
+       const char *brief;
 
        if (!want)
                return want;
 
-       brief = getenv(TR2_ENVVAR_NORMAL_BRIEF);
+       brief = tr2_sysenv_get(TR2_SYSENV_NORMAL_BRIEF);
        if (brief && *brief &&
            ((want_brief = git_parse_maybe_bool(brief)) != -1))
-               tr2env_normal_brief = want_brief;
+               tr2env_normal_be_brief = want_brief;
 
        return want;
 }
@@ -47,7 +47,7 @@ static void normal_fmt_prepare(const char *file, int line, struct strbuf *buf)
 {
        strbuf_setlen(buf, 0);
 
-       if (!tr2env_normal_brief) {
+       if (!tr2env_normal_be_brief) {
                struct tr2_tbuf tb_now;
 
                tr2_tbuf_local_time(&tb_now);
@@ -81,7 +81,8 @@ static void fn_version_fl(const char *file, int line)
        strbuf_release(&buf_payload);
 }
 
-static void fn_start_fl(const char *file, int line, const char **argv)
+static void fn_start_fl(const char *file, int line,
+                       uint64_t us_elapsed_absolute, const char **argv)
 {
        struct strbuf buf_payload = STRBUF_INIT;
 
index 2a866d701b201d36521cbd7d860d23f378a0f5b7..ea0cbbe13ee06629b2619704a4f9ae6714dc1b7d 100644 (file)
@@ -6,20 +6,20 @@
 #include "json-writer.h"
 #include "trace2/tr2_dst.h"
 #include "trace2/tr2_sid.h"
+#include "trace2/tr2_sysenv.h"
 #include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 
-static struct tr2_dst tr2dst_perf = { "GIT_TR2_PERF", 0, 0, 0 };
+static struct tr2_dst tr2dst_perf = { TR2_SYSENV_PERF, 0, 0, 0 };
 
 /*
- * Set this environment variable to true to omit the "<time> <file>:<line>"
+ * Use TR2_SYSENV_PERF_BRIEF to omit the "<time> <file>:<line>"
  * fields from each line written to the builtin performance target.
  *
  * Unit tests may want to use this to help with testing.
  */
-#define TR2_ENVVAR_PERF_BRIEF "GIT_TR2_PERF_BRIEF"
-static int tr2env_perf_brief;
+static int tr2env_perf_be_brief;
 
 #define TR2FMT_PERF_FL_WIDTH (50)
 #define TR2FMT_PERF_MAX_EVENT_NAME (12)
@@ -36,17 +36,17 @@ static int fn_init(void)
 {
        int want = tr2_dst_trace_want(&tr2dst_perf);
        int want_brief;
-       char *brief;
+       const char *brief;
 
        if (!want)
                return want;
 
        strbuf_addchars(&dots, '.', TR2_DOTS_BUFFER_SIZE);
 
-       brief = getenv(TR2_ENVVAR_PERF_BRIEF);
+       brief = tr2_sysenv_get(TR2_SYSENV_PERF_BRIEF);
        if (brief && *brief &&
            ((want_brief = git_parse_maybe_bool(brief)) != -1))
-               tr2env_perf_brief = want_brief;
+               tr2env_perf_be_brief = want_brief;
 
        return want;
 }
@@ -77,7 +77,7 @@ static void perf_fmt_prepare(const char *event_name,
 
        strbuf_setlen(buf, 0);
 
-       if (!tr2env_perf_brief) {
+       if (!tr2env_perf_be_brief) {
                struct tr2_tbuf tb_now;
 
                tr2_tbuf_local_time(&tb_now);
@@ -159,15 +159,16 @@ static void fn_version_fl(const char *file, int line)
        strbuf_release(&buf_payload);
 }
 
-static void fn_start_fl(const char *file, int line, const char **argv)
+static void fn_start_fl(const char *file, int line,
+                       uint64_t us_elapsed_absolute, const char **argv)
 {
        const char *event_name = "start";
        struct strbuf buf_payload = STRBUF_INIT;
 
        sq_quote_argv_pretty(&buf_payload, argv);
 
-       perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
-                        &buf_payload);
+       perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
+                        NULL, NULL, &buf_payload);
        strbuf_release(&buf_payload);
 }
 
index 8e65b0361dbedb7360d3d0f30229875faf5088d4..e76d8c5d92aec764280506bb78cd7e4a3e48eeb2 100644 (file)
 #define TR2_REGION_NESTING_INITIAL_SIZE (100)
 
 static struct tr2tls_thread_ctx *tr2tls_thread_main;
-static uint64_t tr2tls_us_start_main;
+static uint64_t tr2tls_us_start_process;
 
 static pthread_mutex_t tr2tls_mutex;
 static pthread_key_t tr2tls_key;
 
 static int tr2_next_thread_id; /* modify under lock */
 
-struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name)
+void tr2tls_start_process_clock(void)
+{
+       if (tr2tls_us_start_process)
+               return;
+
+       /*
+        * Keep the absolute start time of the process (i.e. the main
+        * process) in a fixed variable since other threads need to
+        * access it.  This allows them to do that without a lock on
+        * main thread's array data (because of reallocs).
+        */
+       tr2tls_us_start_process = getnanotime() / 1000;
+}
+
+struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name,
+                                            uint64_t us_thread_start)
 {
-       uint64_t us_now = getnanotime() / 1000;
        struct tr2tls_thread_ctx *ctx = xcalloc(1, sizeof(*ctx));
 
        /*
@@ -29,7 +43,7 @@ struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name)
         */
        ctx->alloc = TR2_REGION_NESTING_INITIAL_SIZE;
        ctx->array_us_start = (uint64_t *)xcalloc(ctx->alloc, sizeof(uint64_t));
-       ctx->array_us_start[ctx->nr_open_regions++] = us_now;
+       ctx->array_us_start[ctx->nr_open_regions++] = us_thread_start;
 
        ctx->thread_id = tr2tls_locked_increment(&tr2_next_thread_id);
 
@@ -55,7 +69,7 @@ struct tr2tls_thread_ctx *tr2tls_get_self(void)
         * here and silently continue.
         */
        if (!ctx)
-               ctx = tr2tls_create_self("unknown");
+               ctx = tr2tls_create_self("unknown", getnanotime() / 1000);
 
        return ctx;
 }
@@ -124,22 +138,18 @@ uint64_t tr2tls_absolute_elapsed(uint64_t us)
        if (!tr2tls_thread_main)
                return 0;
 
-       return us - tr2tls_us_start_main;
+       return us - tr2tls_us_start_process;
 }
 
 void tr2tls_init(void)
 {
+       tr2tls_start_process_clock();
+
        pthread_key_create(&tr2tls_key, NULL);
        init_recursive_mutex(&tr2tls_mutex);
 
-       tr2tls_thread_main = tr2tls_create_self("main");
-       /*
-        * Keep a copy of the absolute start time of the main thread
-        * in a fixed variable since other threads need to access it.
-        * This also eliminates the need to lock accesses to the main
-        * thread's array (because of reallocs).
-        */
-       tr2tls_us_start_main = tr2tls_thread_main->array_us_start[0];
+       tr2tls_thread_main =
+               tr2tls_create_self("main", tr2tls_us_start_process);
 }
 
 void tr2tls_release(void)
index bb80e3f8e7570533883e71741b9aa35d43362599..b1e327a928e2ba084714770c85c15a219dab35f3 100644 (file)
@@ -31,7 +31,8 @@ struct tr2tls_thread_ctx {
  * In this and all following functions the term "self" refers to the
  * current thread.
  */
-struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name);
+struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name,
+                                            uint64_t us_thread_start);
 
 /*
  * Get our TLS data.
@@ -94,4 +95,9 @@ void tr2tls_release(void);
  */
 int tr2tls_locked_increment(int *p);
 
+/*
+ * Capture the process start time and do nothing else.
+ */
+void tr2tls_start_process_clock(void);
+
 #endif /* TR2_TLS_H */
index 365ea574c7b52e0fc4381295eed6650ca5f67a87..f1fcd2c4b006dc2ece2019ac91f73a2f42bbf6bd 100644 (file)
@@ -252,6 +252,14 @@ static int connect_setup(struct transport *transport, int for_push)
        return 0;
 }
 
+static void die_if_server_options(struct transport *transport)
+{
+       if (!transport->server_options || !transport->server_options->nr)
+               return;
+       advise(_("see protocol.version in 'git help config' for more details"));
+       die(_("server options require protocol version 2 or later"));
+}
+
 /*
  * Obtains the protocol version from the transport and writes it to
  * transport->data->version, first connecting if not already connected.
@@ -286,6 +294,7 @@ static struct ref *handshake(struct transport *transport, int for_push,
                break;
        case protocol_v1:
        case protocol_v0:
+               die_if_server_options(transport);
                get_remote_heads(&reader, &refs,
                                 for_push ? REF_NORMAL : 0,
                                 &data->extra_have,
@@ -362,6 +371,7 @@ static int fetch_refs_via_pack(struct transport *transport,
                break;
        case protocol_v1:
        case protocol_v0:
+               die_if_server_options(transport);
                refs = fetch_pack(&args, data->fd,
                                  refs_tmp ? refs_tmp : transport->remote_refs,
                                  to_fetch, nr_heads, &data->shallow,
index f2ee7c4f499a0937ba4c7148db045665fcb7e4cf..06e06d3d8937bc553ef42cd895ee7132c6e5639c 100644 (file)
@@ -134,7 +134,7 @@ struct transport {
 #define TRANSPORT_PUSH_OPTIONS                 (1<<14)
 #define TRANSPORT_RECURSE_SUBMODULES_ONLY      (1<<15)
 
-extern int transport_summary_width(const struct ref *refs);
+int transport_summary_width(const struct ref *refs);
 
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
@@ -263,5 +263,5 @@ 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 struct object_id *oid, void *);
-extern void for_each_alternate_ref(alternate_ref_fn, void *);
+void for_each_alternate_ref(alternate_ref_fn, void *);
 #endif
index e6d306f69f940edd9613de0b38ffe2d9993f91ab..f1f641eb6a64abc2dbb6f0ef70f7375101874812 100644 (file)
@@ -181,7 +181,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
        struct tree_desc *t, struct tree_desc *tp,
        int imin)
 {
-       unsigned mode;
+       unsigned short mode;
        const char *path;
        const struct object_id *oid;
        int pathlen;
index 1e4bbc8a0e48c6afe61dc8aab76488356d7e1cbc..ec32a47b2e7664365f771f3955747794001d3f28 100644 (file)
@@ -500,7 +500,7 @@ struct dir_state {
        struct object_id oid;
 };
 
-static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned *mode)
+static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned short *mode)
 {
        int namelen = strlen(name);
        while (t->size) {
@@ -535,7 +535,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, struct object_
        return -1;
 }
 
-int get_tree_entry(const struct object_id *tree_oid, const char *name, struct object_id *oid, unsigned *mode)
+int get_tree_entry(const struct object_id *tree_oid, const char *name, struct object_id *oid, unsigned short *mode)
 {
        int retval;
        void *tree;
@@ -585,7 +585,7 @@ int get_tree_entry(const struct object_id *tree_oid, const char *name, struct ob
  * See the code for enum get_oid_result for a description of
  * the return values.
  */
-enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode)
+enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode)
 {
        int retval = MISSING_OBJECT;
        struct dir_state *parents = NULL;
index 82251718666eb5dceb33d6140e86de5a627baab1..161e2400f443460fc6606e97d4e356c1f0b478ed 100644 (file)
@@ -16,7 +16,7 @@ struct tree_desc {
        unsigned int size;
 };
 
-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)
 {
        *pathp = desc->entry.path;
        *modep = desc->entry.mode;
@@ -51,7 +51,7 @@ struct traverse_info;
 typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
 int traverse_trees(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info);
 
-enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode);
+enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode);
 
 struct traverse_info {
        const char *traverse_path;
@@ -66,9 +66,9 @@ struct traverse_info {
        int show_all_errors;
 };
 
-int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned *);
-extern char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);
-extern void setup_traverse_info(struct traverse_info *info, const char *base);
+int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned short *);
+char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);
+void setup_traverse_info(struct traverse_info *info, const char *base);
 
 static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n)
 {
index cb603a6d8aeaeb4ccbae0968df6bf13813cc2a51..d2ea5eb20d90557de4dc979d98a9479e0c2e7d9b 100644 (file)
@@ -839,7 +839,7 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not,
        if (skip_prefix(line, "deepen-not ", &arg)) {
                char *ref = NULL;
                struct object_id oid;
-               if (expand_ref(arg, strlen(arg), &oid, &ref) != 1)
+               if (expand_ref(the_repository, arg, strlen(arg), &oid, &ref) != 1)
                        die("git upload-pack: ambiguous deepen-not: %s", line);
                string_list_append(deepen_not, ref);
                free(ref);
index cab2178796a6406ed154c56bc9e06e54ba41c6f6..4bafe16a22c37d3eac65690cc6f1cc68476cf94c 100644 (file)
@@ -13,11 +13,11 @@ void upload_pack(struct upload_pack_options *options);
 struct repository;
 struct argv_array;
 struct packet_reader;
-extern int upload_pack_v2(struct repository *r, struct argv_array *keys,
-                         struct packet_reader *request);
+int upload_pack_v2(struct repository *r, struct argv_array *keys,
+                  struct packet_reader *request);
 
 struct strbuf;
-extern int upload_pack_advertise(struct repository *r,
-                                struct strbuf *value);
+int upload_pack_advertise(struct repository *r,
+                         struct strbuf *value);
 
 #endif /* UPLOAD_PACK_H */
diff --git a/url.h b/url.h
index f311c40a4680bfedf0ad2d511a5599fb5dcabd6e..00b7d58c33e38b2a48e4e2ab557239b1ed0c759d 100644 (file)
--- a/url.h
+++ b/url.h
@@ -3,14 +3,14 @@
 
 struct strbuf;
 
-extern int is_url(const char *url);
-extern int is_urlschemechar(int first_flag, int ch);
-extern char *url_decode(const char *url);
-extern char *url_decode_mem(const char *url, int len);
-extern char *url_decode_parameter_name(const char **query);
-extern char *url_decode_parameter_value(const char **query);
+int is_url(const char *url);
+int is_urlschemechar(int first_flag, int ch);
+char *url_decode(const char *url);
+char *url_decode_mem(const char *url, int len);
+char *url_decode_parameter_name(const char **query);
+char *url_decode_parameter_value(const char **query);
 
-extern void end_url_with_slash(struct strbuf *buf, const char *url);
-extern void str_end_url_with_slash(const char *url, char **dest);
+void end_url_with_slash(struct strbuf *buf, const char *url);
+void str_end_url_with_slash(const char *url, char **dest);
 
 #endif /* URL_H */
index e482148248d2dac64656bd9a66af0bd6224ea929..eed5f292354cfef466f15f29ef944450e10d4d1a 100644 (file)
@@ -33,7 +33,7 @@ struct url_info {
                                 * '?...' and '#...' portion; will always be >= 1 */
 };
 
-extern char *url_normalize(const char *, struct url_info *);
+char *url_normalize(const char *, struct url_info *);
 
 struct urlmatch_item {
        size_t hostmatch_len;
@@ -52,6 +52,6 @@ struct urlmatch_config {
        int (*cascade_fn)(const char *var, const char *value, void *cb);
 };
 
-extern int urlmatch_config_entry(const char *var, const char *value, void *cb);
+int urlmatch_config_entry(const char *var, const char *value, void *cb);
 
 #endif /* URL_MATCH_H */
diff --git a/utf8.h b/utf8.h
index 84efbfcb1fa163b5b799add81b4464ff6eb88611..fcd5167bafb38ddafc3c4e22b642b87fb935c8e1 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -16,7 +16,7 @@ __attribute__((format (printf, 2, 3)))
 int utf8_fprintf(FILE *, const char *, ...);
 
 extern const char utf8_bom[];
-extern int skip_utf8_bom(char **, size_t);
+int skip_utf8_bom(char **, size_t);
 
 void strbuf_add_wrapped_text(struct strbuf *buf,
                const char *text, int indent, int indent2, int width);
index c1c44d9a6bde4853f55d6c8d7cd59ad159ef4abb..f78bb0ca528dcd2b955fba8ca1d9e3db7f776abb 100644 (file)
--- a/varint.h
+++ b/varint.h
@@ -1,7 +1,7 @@
 #ifndef VARINT_H
 #define VARINT_H
 
-extern int encode_varint(uintmax_t, unsigned char *);
-extern uintmax_t decode_varint(const unsigned char **);
+int encode_varint(uintmax_t, unsigned char *);
+uintmax_t decode_varint(const unsigned char **);
 
 #endif /* VARINT_H */
index 189c32d84c3c654c0a16bfe71284b24029724019..a7fc0999cb63c2abd9dd01f14aacabf40d986b13 100644 (file)
@@ -13,6 +13,6 @@ struct sliding_view {
 
 #define SLIDING_VIEW_INIT(input, len)  { (input), 0, 0, (len), STRBUF_INIT }
 
-extern int move_window(struct sliding_view *view, off_t off, size_t width);
+int move_window(struct sliding_view *view, off_t off, size_t width);
 
 #endif
index 10a2cbc40ec1c1ec7a97e967e3420b0b819f6641..625d950bb89405a5607b4d313d4ac3f80f14c49c 100644 (file)
@@ -4,7 +4,7 @@
 struct line_buffer;
 struct sliding_view;
 
-extern int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
-               struct sliding_view *preimage, FILE *postimage);
+int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
+                  struct sliding_view *preimage, FILE *postimage);
 
 #endif
index b45bfeb9d33b4ae4f372a15edd90651bf3735f92..4f66cd9ce178d855245712b42d681ff8bcda0636 100644 (file)
@@ -49,18 +49,24 @@ static struct worktree *get_main_worktree(void)
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf worktree_path = STRBUF_INIT;
-       int is_bare = 0;
 
        strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
-       is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
-       if (is_bare)
+       if (!strbuf_strip_suffix(&worktree_path, "/.git"))
                strbuf_strip_suffix(&worktree_path, "/.");
 
        strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
 
        worktree = xcalloc(1, sizeof(*worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
-       worktree->is_bare = is_bare;
+       /*
+        * NEEDSWORK: If this function is called from a secondary worktree and
+        * config.worktree is present, is_bare_repository_cfg will reflect the
+        * contents of config.worktree, not the contents of the main worktree.
+        * This means that worktree->is_bare may be set to 0 even if the main
+        * worktree is configured to be bare.
+        */
+       worktree->is_bare = (is_bare_repository_cfg == 1) ||
+               is_bare_repository();
        add_head_info(worktree);
 
        strbuf_release(&path);
index 9e3b0b7b6f9bd2aad3de9094febb8ad76aa45caf..caecc7a281cc6778447a5c70bc7a756306bbbd3b 100644 (file)
@@ -30,37 +30,37 @@ struct worktree {
  * The caller is responsible for freeing the memory from the returned
  * worktree(s).
  */
-extern struct worktree **get_worktrees(unsigned flags);
+struct worktree **get_worktrees(unsigned flags);
 
 /*
  * Returns 1 if linked worktrees exist, 0 otherwise.
  */
-extern int submodule_uses_worktrees(const char *path);
+int submodule_uses_worktrees(const char *path);
 
 /*
  * Return git dir of the worktree. Note that the path may be relative.
  * If wt is NULL, git dir of current worktree is returned.
  */
-extern const char *get_worktree_git_dir(const struct worktree *wt);
+const char *get_worktree_git_dir(const struct worktree *wt);
 
 /*
  * Search a worktree that can be unambiguously identified by
  * "arg". "prefix" must not be NULL.
  */
-extern struct worktree *find_worktree(struct worktree **list,
-                                     const char *prefix,
-                                     const char *arg);
+struct worktree *find_worktree(struct worktree **list,
+                              const char *prefix,
+                              const char *arg);
 
 /*
  * Return true if the given worktree is the main one.
  */
-extern int is_main_worktree(const struct worktree *wt);
+int is_main_worktree(const struct worktree *wt);
 
 /*
  * Return the reason string if the given worktree is locked or NULL
  * otherwise.
  */
-extern const char *worktree_lock_reason(struct worktree *wt);
+const char *worktree_lock_reason(struct worktree *wt);
 
 #define WT_VALIDATE_WORKTREE_MISSING_OK (1 << 0)
 
@@ -68,28 +68,28 @@ extern const char *worktree_lock_reason(struct worktree *wt);
  * Return zero if the worktree is in good condition. Error message is
  * returned if "errmsg" is not NULL.
  */
-extern int validate_worktree(const struct worktree *wt,
-                            struct strbuf *errmsg,
-                            unsigned flags);
+int validate_worktree(const struct worktree *wt,
+                     struct strbuf *errmsg,
+                     unsigned flags);
 
 /*
  * Update worktrees/xxx/gitdir with the new path.
  */
-extern void update_worktree_location(struct worktree *wt,
-                                    const char *path_);
+void update_worktree_location(struct worktree *wt,
+                             const char *path_);
 
 /*
  * Free up the memory for worktree(s)
  */
-extern void free_worktrees(struct worktree **);
+void free_worktrees(struct worktree **);
 
 /*
  * Check if a per-worktree symref points to a ref in the main worktree
  * or any linked worktree, and return the worktree that holds the ref,
  * or NULL otherwise. The result may be destroyed by the next call.
  */
-extern const struct worktree *find_shared_symref(const char *symref,
-                                                const char *target);
+const struct worktree *find_shared_symref(const char *symref,
+                                         const char *target);
 
 /*
  * Similar to head_ref() for all HEADs _except_ one from the current
@@ -104,8 +104,8 @@ int is_worktree_being_bisected(const struct worktree *wt, const char *target);
  * Similar to git_path() but can produce paths for a specified
  * worktree instead of current one
  */
-extern const char *worktree_git_path(const struct worktree *wt,
-                                    const char *fmt, ...)
+const char *worktree_git_path(const struct worktree *wt,
+                             const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 
 /*
index 445a36204a69cf69f2d3e87ea6d308a6b5a9ab29..e065558c312eefb4c3d5f8bf91dd8358069e0faa 100644 (file)
@@ -17,6 +17,7 @@
 #include "utf8.h"
 #include "worktree.h"
 #include "lockfile.h"
+#include "sequencer.h"
 
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
@@ -1006,13 +1007,19 @@ size_t wt_status_locate_end(const char *s, size_t len)
        return len;
 }
 
-void wt_status_add_cut_line(FILE *fp)
+void wt_status_append_cut_line(struct strbuf *buf)
 {
        const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored.");
+
+       strbuf_commented_addf(buf, "%s", cut_line);
+       strbuf_add_commented_lines(buf, explanation, strlen(explanation));
+}
+
+void wt_status_add_cut_line(FILE *fp)
+{
        struct strbuf buf = STRBUF_INIT;
 
-       fprintf(fp, "%c %s", comment_line_char, cut_line);
-       strbuf_add_commented_lines(&buf, explanation, strlen(explanation));
+       wt_status_append_cut_line(&buf);
        fputs(buf.buf, fp);
        strbuf_release(&buf);
 }
@@ -1380,12 +1387,22 @@ static void show_rebase_in_progress(struct wt_status *s,
 static void show_cherry_pick_in_progress(struct wt_status *s,
                                         const char *color)
 {
-       status_printf_ln(s, color, _("You are currently cherry-picking commit %s."),
-                       find_unique_abbrev(&s->state.cherry_pick_head_oid, DEFAULT_ABBREV));
+       if (is_null_oid(&s->state.cherry_pick_head_oid))
+               status_printf_ln(s, color,
+                       _("Cherry-pick currently in progress."));
+       else
+               status_printf_ln(s, color,
+                       _("You are currently cherry-picking commit %s."),
+                       find_unique_abbrev(&s->state.cherry_pick_head_oid,
+                                          DEFAULT_ABBREV));
+
        if (s->hints) {
                if (has_unmerged(s))
                        status_printf_ln(s, color,
                                _("  (fix conflicts and run \"git cherry-pick --continue\")"));
+               else if (is_null_oid(&s->state.cherry_pick_head_oid))
+                       status_printf_ln(s, color,
+                               _("  (run \"git cherry-pick --continue\" to continue)"));
                else
                        status_printf_ln(s, color,
                                _("  (all conflicts fixed: run \"git cherry-pick --continue\")"));
@@ -1398,12 +1415,21 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
 static void show_revert_in_progress(struct wt_status *s,
                                    const char *color)
 {
-       status_printf_ln(s, color, _("You are currently reverting commit %s."),
-                        find_unique_abbrev(&s->state.revert_head_oid, DEFAULT_ABBREV));
+       if (is_null_oid(&s->state.revert_head_oid))
+               status_printf_ln(s, color,
+                       _("Revert currently in progress."));
+       else
+               status_printf_ln(s, color,
+                       _("You are currently reverting commit %s."),
+                       find_unique_abbrev(&s->state.revert_head_oid,
+                                          DEFAULT_ABBREV));
        if (s->hints) {
                if (has_unmerged(s))
                        status_printf_ln(s, color,
                                _("  (fix conflicts and run \"git revert --continue\")"));
+               else if (is_null_oid(&s->state.revert_head_oid))
+                       status_printf_ln(s, color,
+                               _("  (run \"git revert --continue\" to continue)"));
                else
                        status_printf_ln(s, color,
                                _("  (all conflicts fixed: run \"git revert --continue\")"));
@@ -1574,6 +1600,7 @@ void wt_status_get_state(struct repository *r,
 {
        struct stat st;
        struct object_id oid;
+       enum replay_action action;
 
        if (!stat(git_path_merge_head(r), &st)) {
                wt_status_check_rebase(NULL, state);
@@ -1591,7 +1618,15 @@ void wt_status_get_state(struct repository *r,
                state->revert_in_progress = 1;
                oidcpy(&state->revert_head_oid, &oid);
        }
-
+       if (!sequencer_get_last_command(r, &action)) {
+               if (action == REPLAY_PICK) {
+                       state->cherry_pick_in_progress = 1;
+                       oidcpy(&state->cherry_pick_head_oid, &null_oid);
+               } else {
+                       state->revert_in_progress = 1;
+                       oidcpy(&state->revert_head_oid, &null_oid);
+               }
+       }
        if (get_detached_from)
                wt_status_get_detached_from(r, state);
 }
@@ -1851,7 +1886,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
        color_fprintf(s->fp, branch_color_local, "%s", branch_name);
 
        sti = stat_tracking_info(branch, &num_ours, &num_theirs, &base,
-                                s->ahead_behind_flags);
+                                0, s->ahead_behind_flags);
        if (sti < 0) {
                if (!base)
                        goto conclude;
@@ -1990,7 +2025,7 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
                branch = branch_get(branch_name);
                base = NULL;
                ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,
-                                            &base, s->ahead_behind_flags);
+                                            &base, 0, s->ahead_behind_flags);
                if (base) {
                        base = shorten_unambiguous_ref(base, 0);
                        fprintf(s->fp, "# branch.upstream %s%c", base, eol);
index 3a95975032bb1ee8d87cca4e8a1bbc0afe62e42c..64f1ddc9fd9950f7dbe3b0889bf2085111814cec 100644 (file)
@@ -129,6 +129,7 @@ struct wt_status {
 };
 
 size_t wt_status_locate_end(const char *s, size_t len);
+void wt_status_append_cut_line(struct strbuf *buf);
 void wt_status_add_cut_line(FILE *fp);
 void wt_status_prepare(struct repository *r, struct wt_status *s);
 void wt_status_print(struct wt_status *s);
index 2d41fffd4c618b5d7b816146d9df684b195535e3..ede4246bbd3397086f90217539a2d07a35a4b986 100644 (file)
@@ -27,9 +27,9 @@ int read_mmfile(mmfile_t *ptr, const char *filename);
 void read_mmblob(mmfile_t *ptr, const struct object_id *oid);
 int buffer_is_binary(const char *ptr, unsigned long size);
 
-extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
-extern void xdiff_clear_find_func(xdemitconf_t *xecfg);
-extern int git_xmerge_config(const char *var, const char *value, void *cb);
+void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
+void xdiff_clear_find_func(xdemitconf_t *xecfg);
+int git_xmerge_config(const char *var, const char *value, void *cb);
 extern int git_xmerge_style;
 
 /*
@@ -46,14 +46,14 @@ void discard_hunk_line(void *priv,
  * The `flags` given as XDF_WHITESPACE_FLAGS determine how white spaces
  * are treated for the comparision.
  */
-extern int xdiff_compare_lines(const char *l1, long s1,
-                              const char *l2, long s2, long flags);
+int xdiff_compare_lines(const char *l1, long s1,
+                       const char *l2, long s2, long flags);
 
 /*
  * Returns a hash of the string s of length len.
  * The `flags` given as XDF_WHITESPACE_FLAGS determine how white spaces
  * are treated for the hash.
  */
-extern unsigned long xdiff_hash_string(const char *s, size_t len, long flags);
+unsigned long xdiff_hash_string(const char *s, size_t len, long flags);
 
 #endif