Merge branch 'jt/packmigrate'
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 Sep 2017 01:47:53 +0000 (10:47 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Sep 2017 01:47:53 +0000 (10:47 +0900)
Remove unneeded file added by a topic already in 'master'.

* jt/packmigrate:
Remove inadvertently added outgoing/packfile.h

161 files changed:
.tsan-suppressions [new file with mode: 0644]
Documentation/RelNotes/2.14.2.txt
Documentation/RelNotes/2.15.0.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-apply.txt
Documentation/git-branch.txt
Documentation/git-cvsserver.txt
Documentation/git-diff-index.txt
Documentation/git-interpret-trailers.txt
Documentation/git-merge.txt
Documentation/git-pack-objects.txt
Documentation/git-rebase.txt
Documentation/git-rerere.txt
Documentation/git-rm.txt
Documentation/git-svn.txt
Documentation/git-update-index.txt
Documentation/gitcore-tutorial.txt
Documentation/githooks.txt
Documentation/gitrepository-layout.txt
Documentation/gittutorial.txt
Documentation/merge-options.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/technical/api-ref-iteration.txt
Documentation/technical/api-tree-walking.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/trivial-merge.txt
Documentation/user-manual.txt
Makefile
apply.c
archive.c
branch.c
builtin/add.c
builtin/blame.c
builtin/branch.c
builtin/checkout.c
builtin/commit.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/difftool.c
builtin/fetch.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/hash-object.c
builtin/interpret-trailers.c
builtin/log.c
builtin/ls-files.c
builtin/merge-tree.c
builtin/merge.c
builtin/mv.c
builtin/name-rev.c
builtin/pack-objects.c
builtin/prune-packed.c
builtin/prune.c
builtin/read-tree.c
builtin/replace.c
builtin/reset.c
builtin/rev-list.c
builtin/rm.c
builtin/submodule--helper.c
builtin/update-index.c
cache-tree.c
cache.h
color.c
color.h
commit.c
commit.h
config.c
config.h
contrib/diff-highlight/Makefile
contrib/examples/git-merge.sh
contrib/examples/git-resolve.sh
contrib/subtree/t/t7900-subtree.sh
convert.c
convert.h
credential-cache--daemon.c
diff.c
diff.h
diffcore-rename.c
entry.c
git-gui/po/README
git-merge-octopus.sh
git-p4.py
git-send-email.perl
gitweb/gitweb.perl
gpg-interface.c
hash.h
http.c
list.h
lockfile.c
lockfile.h
merge-recursive.c
notes-merge.c
notes.c
pkt-line.c
pretty.c
progress.c
progress.h
reachable.c
read-cache.c
refs.c
refs.h
refs/files-backend.c
refs/packed-backend.c
refs/refs-internal.h
remote.c
rerere.c
revision.c
revision.h
sha1-lookup.h
sha1_file.c
sha1dc_git.c
sha1dc_git.h
shallow.c
strbuf.h
sub-process.c
submodule-config.c
submodule-config.h
submodule.c
submodule.h
t/helper/test-submodule-config.c
t/helper/test-write-cache.c [new file with mode: 0644]
t/perf/p0007-write-cache.sh [new file with mode: 0755]
t/t1407-worktree-ref-store.sh
t/t3200-branch.sh
t/t3903-stash.sh
t/t4015-diff-whitespace.sh
t/t4027-diff-submodule.sh
t/t4124-apply-ws-rule.sh
t/t4200-rerere.sh
t/t4205-log-pretty-formats.sh
t/t5001-archive-attr.sh
t/t5304-prune.sh
t/t6040-tracking-info.sh
t/t7400-submodule-basic.sh
t/t7411-submodule-config.sh
t/t7513-interpret-trailers.sh
t/t7600-merge.sh
t/t7614-merge-signoff.sh [new file with mode: 0755]
t/t9001-send-email.sh
tempfile.c
tempfile.h
templates/hooks--pre-rebase.sample
trailer.c
trailer.h
transport-helper.c
tree-diff.c
tree-walk.c
tree-walk.h
unpack-trees.c
vcs-svn/fast_export.c
vcs-svn/fast_export.h
vcs-svn/repo_tree.c [deleted file]
vcs-svn/repo_tree.h [deleted file]
vcs-svn/svndump.c
worktree.c
worktree.h
diff --git a/.tsan-suppressions b/.tsan-suppressions
new file mode 100644 (file)
index 0000000..8c85014
--- /dev/null
@@ -0,0 +1,10 @@
+# Suppressions for ThreadSanitizer (tsan).
+#
+# This file is used by setting the environment variable TSAN_OPTIONS to, e.g.,
+# "suppressions=$(pwd)/.tsan-suppressions". Observe that relative paths such as
+# ".tsan-suppressions" might not work.
+
+# A static variable is written to racily, but we always write the same value, so
+# in practice it (hopefully!) doesn't matter.
+race:^want_color$
+race:^transfer_debug$
index 5517afcf5920b8fa023e2ba0babf6c4a1d92509b..bcfe78f59dd0cfb24897d986499aa7c8ada12834 100644 (file)
@@ -32,4 +32,63 @@ Fixes since v2.14.1
    daemon is torn down were flaky.  This was fixed by reacting to
    ECONNRESET and behaving as if we got an EOF.
 
+ * Some versions of GnuPG fail to kill gpg-agent it auto-spawned
+   and such a left-over agent can interfere with a test.  Work it
+   around by attempting to kill one before starting a new test.
+
+ * "git log --tag=no-such-tag" showed log starting from HEAD, which
+   has been fixed---it now shows nothing.
+
+ * The "tag.pager" configuration variable was useless for those who
+   actually create tag objects, as it interfered with the use of an
+   editor.  A new mechanism has been introduced for commands to enable
+   pager depending on what operation is being carried out to fix this,
+   and then "git tag -l" is made to run pager by default.
+
+ * "git push --recurse-submodules $there HEAD:$target" was not
+   propagated down to the submodules, but now it is.
+
+ * Commands like "git rebase" accepted the --rerere-autoupdate option
+   from the command line, but did not always use it.  This has been
+   fixed.
+
+ * "git clone --recurse-submodules --quiet" did not pass the quiet
+   option down to submodules.
+
+ * "git am -s" has been taught that some input may end with a trailer
+   block that is not Signed-off-by: and it should refrain from adding
+   an extra blank line before adding a new sign-off in such a case.
+
+ * "git svn" used with "--localtime" option did not compute the tz
+   offset for the timestamp in question and instead always used the
+   current time, which has been corrected.
+
+ * Memory leaks in a few error codepaths have been plugged.
+
+ * bash 4.4 or newer gave a warning on NUL byte in command
+   substitution done in "git stash"; this has been squelched.
+
+ * "git grep -L" and "git grep --quiet -L" reported different exit
+   codes; this has been corrected.
+
+ * When handshake with a subprocess filter notices that the process
+   asked for an unknown capability, Git did not report what program
+   the offending subprocess was running.  This has been corrected.
+
+ * "git apply" that is used as a better "patch -p1" failed to apply a
+   taken from a file with CRLF line endings to a file with CRLF line
+   endings.  The root cause was because it misused convert_to_git()
+   that tried to do "safe-crlf" processing by looking at the index
+   entry at the same path, which is a nonsense---in that mode, "apply"
+   is not working on the data in (or derived from) the index at all.
+   This has been fixed.
+
+ * Killing "git merge --edit" before the editor returns control left
+   the repository in a state with MERGE_MSG but without MERGE_HEAD,
+   which incorrectly tells the subsequent "git commit" that there was
+   a squash merge in progress.  This has been fixed.
+
+ * "git archive" did not work well with pathspecs and the
+   export-ignore attribute.
+
 Also contains various documentation updates and code clean-ups.
index 2fd49978d7641184089581fc8ee1592e4d898121..31f7b0ed0395fa92e2ba4e854dd3390776a53f9a 100644 (file)
@@ -18,6 +18,9 @@ Backward compatibility notes and other notable changes.
    might still be cases that need to be addressed--bug reports are
    greatly appreciated.
 
+ * "branch --set-upstream" that has been deprecated in Git 1.8 has
+   finally been retired.
+
 
 Updates since v2.14
 -------------------
@@ -33,13 +36,11 @@ UI, Workflows & Features
 
  * The "rerere-train" script (in contrib/) learned the "--overwrite"
    option to allow overwriting existing recorded resolutions.
-   (merge ad53bf79aa rg/rerere-train-overwrite later to maint).
 
  * "git contacts" (in contrib/) now lists the address on the
    "Reported-by:" trailer to its output, in addition to those on
    S-o-b: and other trailers, to make it easier to notify (and thank)
    the original bug reporter.
-   (merge 09ac673788 eb/contacts-reported-by later to maint).
 
  * "git rebase", especially when it is run by mistake and ends up
    trying to replay many changes, spent long time in silence.  The
@@ -47,6 +48,38 @@ UI, Workflows & Features
    long time preparing these many changes to replay (which would give
    the user a chance to abort with ^C).
 
+ * "git merge" learned a "--signoff" option to add the Signed-off-by:
+   trailer with the committer's name.
+
+ * "git diff" learned to optionally paint new lines that are the same
+   as deleted lines elsewhere differently from genuinely new lines.
+
+ * "git interpret-trailers" learned to take the trailer specifications
+   from the command line that overrides the configured values.
+
+ * "git interpret-trailers" has been taught a "--parse" and a few
+   other options to make it easier for scripts to grab existing
+   trailer lines from a commit log message.
+
+ * "gitweb" shows a link to visit the 'raw' contents of blbos in the
+   history overview page.
+
+ * "[gc] rerereResolved = 5.days" used to be invalid, as the variable
+   is defined to take an integer counting the number of days.  It now
+   is allowed.
+
+ * The code to acquire a lock on a reference (e.g. while accepting a
+   push from a client) used to immediately fail when the reference is
+   already locked---now it waits for a very short while and retries,
+   which can make it succeed if the lock holder was holding it during
+   a read-only operation.
+
+ * "branch --set-upstream" that has been deprecated in Git 1.8 has
+   finally been retired.
+
+ * The codepath to call external process filter for smudge/clean
+   operation learned to show the progress meter.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -69,7 +102,6 @@ Performance, Internal Implementation, Development Support etc.
  * Because recent Git for Windows do come with a real msgfmt, the
    build procedure for git-gui has been updated to use it instead of a
    hand-rolled substitute.
-   (merge 90dbf226ba js/git-gui-msgfmt-on-windows later to maint).
 
  * "git grep --recurse-submodules" has been reworked to give a more
    consistent output across submodule boundary (and do its thing
@@ -87,6 +119,22 @@ Performance, Internal Implementation, Development Support etc.
    has been optimized out when we know we do not run the pre-commit hook.
    (merge 680ee550d7 kw/commit-keep-index-when-pre-commit-is-not-run later to maint).
 
+ * Updates to the HTTP layer we made recently unconditionally used
+   features of libCurl without checking the existence of them, causing
+   compilation errors, which has been fixed.  Also migrate the code to
+   check feature macros, not version numbers, to cope better with
+   libCurl that vendor ships with backported features.
+
+ * The API to start showing progress meter after a short delay has
+   been simplified.
+   (merge 8aade107dd jc/simplify-progress later to maint).
+
+ * Code clean-up to avoid mixing values read from the .gitmodules file
+   and values read from the .git/config file.
+
+ * We used to spend more than necessary cycles allocating and freeing
+   piece of memory while writing each index entry out.  This has been
+   optimized.
 
 Also contains various documentation updates and code clean-ups.
 
@@ -98,82 +146,62 @@ Fixes since v2.14
    color escape codes, which was an early design mistake.  They now
    honor the configuration (e.g. "color.ui = never") and also tty-ness
    of the output medium.
-   (merge 11b087adfd jk/ref-filter-colors later to maint).
 
  * The http.{sslkey,sslCert} configuration variables are to be
    interpreted as a pathname that honors "~[username]/" prefix, but
    weren't, which has been fixed.
-   (merge 8d1549643e jc/http-sslkey-and-ssl-cert-are-paths later to maint).
 
  * Numerous bugs in walking of reflogs via "log -g" and friends have
    been fixed.
-   (merge de239446b6 jk/reflog-walk later to maint).
 
  * "git commit" when seeing an totally empty message said "you did not
    edit the message", which is clearly wrong.  The message has been
    corrected.
-   (merge bc17f35f8c ks/commit-abort-on-empty-message-fix later to maint).
 
  * When a directory is not readable, "gitweb" fails to build the
    project list.  Work this around by skipping such a directory.
-   (merge 46a13857fc hb/gitweb-project-list later to maint).
 
  * Some versions of GnuPG fails to kill gpg-agent it auto-spawned
    and such a left-over agent can interfere with a test.  Work it
    around by attempting to kill one before starting a new test.
-   (merge 29ff1f8f74 st/lib-gpg-kill-stray-agent later to maint).
 
  * A recently added test for the "credential-cache" helper revealed
    that EOF detection done around the time the connection to the cache
    daemon is torn down were flaky.  This was fixed by reacting to
    ECONNRESET and behaving as if we got an EOF.
-   (merge 1f180e5eb9 dl/credential-cache-socket-in-xdg-cache later to maint).
 
  * "git log --tag=no-such-tag" showed log starting from HEAD, which
    has been fixed---it now shows nothing.
-   (merge 5d34d1ac06 jk/rev-list-empty-input later to maint).
 
  * The "tag.pager" configuration variable was useless for those who
    actually create tag objects, as it interfered with the use of an
    editor.  A new mechanism has been introduced for commands to enable
    pager depending on what operation is being carried out to fix this,
    and then "git tag -l" is made to run pager by default.
-   (merge 595d59e2b5 ma/pager-per-subcommand-action later to maint).
 
  * "git push --recurse-submodules $there HEAD:$target" was not
    propagated down to the submodules, but now it is.
-   (merge c7be7201a7 bw/push-options-recursively-to-submodules later to maint).
 
  * Commands like "git rebase" accepted the --rerere-autoupdate option
    from the command line, but did not always use it.  This has been
    fixed.
-   (merge f826fb799e pw/sequence-rerere-autoupdate later to maint).
 
  * "git clone --recurse-submodules --quiet" did not pass the quiet
    option down to submodules.
-   (merge 03c004c581 bw/clone-recursive-quiet later to maint).
 
  * Test portability fix for OBSD.
-   (merge bed67874e2 rs/obsd-getcwd-workaround later to maint).
-   (merge 4c7fda8fc1 rs/t4062-obsd later to maint).
 
  * Portability fix for OBSD.
-   (merge 29c2eda80b rs/in-obsd-basename-dirname-take-const later to maint).
 
  * "git am -s" has been taught that some input may end with a trailer
    block that is not Signed-off-by: and it should refrain from adding
    an extra blank line before adding a new sign-off in such a case.
-   (merge 735285b403 pw/am-signoff later to maint).
 
  * "git svn" used with "--localtime" option did not compute the tz
    offset for the timestamp in question and instead always used the
    current time, which has been corrected.
-   (merge 1adc4b9a58 ur/svn-local-zone later to maint).
 
  * Memory leak in an error codepath has been plugged.
-   (merge 83cd6f9017 rs/fsck-obj-leakfix later to maint).
-   (merge 896dca3ab7 rs/unpack-entry-leakfix later to maint).
-   (merge 149d8cbb2e rs/win32-syslog-leakfix later to maint).
 
  * "git stash -u" used the contents of the committed version of the
    ".gitignore" file to decide which paths are ignored, even when the
@@ -182,39 +210,44 @@ Fixes since v2.14
 
  * bash 4.4 or newer gave a warning on NUL byte in command
    substitution done in "git stash"; this has been squelched.
-   (merge 5fc92f8828 kd/stash-with-bash-4.4 later to maint).
 
  * "git grep -L" and "git grep --quiet -L" reported different exit
    codes; this has been corrected.
-   (merge e1f68c66d5 as/grep-quiet-no-match-exit-code-fix later to maint).
+
+ * When handshake with a subprocess filter notices that the process
+   asked for an unknown capability, Git did not report what program
+   the offending subprocess was running.  This has been corrected.
+
+ * "git apply" that is used as a better "patch -p1" failed to apply a
+   taken from a file with CRLF line endings to a file with CRLF line
+   endings.  The root cause was because it misused convert_to_git()
+   that tried to do "safe-crlf" processing by looking at the index
+   entry at the same path, which is a nonsense---in that mode, "apply"
+   is not working on the data in (or derived from) the index at all.
+   This has been fixed.
+
+ * Killing "git merge --edit" before the editor returns control left
+   the repository in a state with MERGE_MSG but without MERGE_HEAD,
+   which incorrectly tells the subsequent "git commit" that there was
+   a squash merge in progress.  This has been fixed.
+
+ * "git archive" did not work well with pathspecs and the
+   export-ignore attribute.
+
+ * In addition to "cc: <a@dd.re.ss> # cruft", "cc: a@dd.re.ss # cruft"
+   was taught to "git send-email" as a valid way to tell it that it
+   needs to also send a carbon copy to <a@dd.re.ss> in the trailer
+   section.
+   (merge cc90750677 mm/send-email-cc-cruft later to maint).
+
+ * "git branch -M a b" while on a branch that is completely unrelated
+   to either branch a or branch b misbehaved when multiple worktree
+   was in use.  This has been fixed.
+   (merge 31824d180d nd/worktree-kill-parse-ref later to maint).
 
  * Other minor doc, test and build updates and code cleanups.
-   (merge 5b114f3bb0 rs/bswap-ubsan-fix later to maint).
-   (merge 168e63554c rs/move-array later to maint).
-   (merge 268ba20110 rs/stat-data-unaligned-reads-fix later to maint).
-   (merge 78e7b98f45 jt/fsck-code-cleanup later to maint).
-   (merge c7b0780545 rs/pack-objects-pbase-cleanup later to maint).
-   (merge c1e860f1dc js/run-process-parallel-api-fix later to maint).
-   (merge 7a40a95eb4 cc/ref-is-hidden-microcleanup later to maint).
-   (merge c0bb6d9cef ah/doc-wserrorhighlight later to maint).
-   (merge edd64ef4f7 dc/fmt-merge-msg-microcleanup later to maint).
-   (merge fa64a2fdbe jt/subprocess-handshake later to maint).
-   (merge 0ba9c9a0fb jb/t8008-cleanup later to maint).
-   (merge a7c28a2161 jt/t1450-fsck-corrupt-packfile later to maint).
-   (merge dff2813391 ab/ref-filter-no-contains later to maint).
    (merge f094b89a4d ma/parse-maybe-bool later to maint).
-   (merge 974ce8078c mf/no-dashed-subcommands later to maint).
-   (merge f81935cc4d jc/perl-git-comment-typofix later to maint).
-   (merge 57ea241ef0 rs/t3700-clean-leftover later to maint).
-   (merge f1068efefe jk/drop-sha1-entry-pos later to maint).
-   (merge 0b006014c8 jk/hashcmp-memcmp later to maint).
-   (merge 1e22a9917b rj/add-chmod-error-message later to maint).
-   (merge 881529c846 rs/apply-lose-prefix-length later to maint).
-   (merge 6355a76802 rs/find-pack-entry-bisection later to maint).
-   (merge de3ce210ed rs/merge-microcleanup later to maint).
-   (merge 7f0a02be2f ah/doc-empty-string-is-false later to maint).
-   (merge 70ec6bd63b rs/t1002-do-not-use-sum later to maint).
-   (merge 2456990dfd sb/sha1-file-cleanup later to maint).
-   (merge 2aac933c62 hv/t5526-andand-chain-fix later to maint).
-   (merge c8d0c4fe9b sb/submodule-parallel-update later to maint).
-   (merge 794b7e1674 mg/format-ref-doc-fix later to maint).
+   (merge 39b00fa4d4 jk/drop-sha1-entry-pos later to maint).
+   (merge 6cdf8a7929 ma/ts-cleanups later to maint).
+   (merge 7560f547e6 ma/up-to-date later to maint).
+   (merge 0db3dc75f3 rs/apply-epoch later to maint).
index 478b9431e0933cc2031e1be01aeab98faa9f55c0..dc4e3f58a2d2e8a5a6753bd290d65fec4f7f7b21 100644 (file)
@@ -776,6 +776,12 @@ core.commentChar::
 If set to "auto", `git-commit` would select a character that is not
 the beginning character of any line in existing commit messages.
 
+core.filesRefLockTimeout::
+       The length of time, in milliseconds, to retry when trying to
+       lock an individual reference. Value 0 means not to retry at
+       all; -1 means to try indefinitely. Default is 100 (i.e.,
+       retry for 100ms).
+
 core.packedRefsTimeout::
        The length of time, in milliseconds, to retry when trying to
        lock the `packed-refs` file. Value 0 means not to retry at
@@ -1077,14 +1083,25 @@ This does not affect linkgit:git-format-patch[1] or the
 'git-diff-{asterisk}' plumbing commands.  Can be overridden on the
 command line with the `--color[=<when>]` option.
 
+diff.colorMoved::
+       If set to either a valid `<mode>` or a true value, moved lines
+       in a diff are colored differently, for details of valid modes
+       see '--color-moved' in linkgit:git-diff[1]. If simply set to
+       true the default color mode will be used. When set to false,
+       moved lines are not colored.
+
 color.diff.<slot>::
        Use customized color for diff colorization.  `<slot>` specifies
        which part of the patch to use the specified color, and is one
        of `context` (context text - `plain` is a historical synonym),
        `meta` (metainformation), `frag`
        (hunk header), 'func' (function in hunk header), `old` (removed lines),
-       `new` (added lines), `commit` (commit headers), or `whitespace`
-       (highlighting whitespace errors).
+       `new` (added lines), `commit` (commit headers), `whitespace`
+       (highlighting whitespace errors), `oldMoved` (deleted lines),
+       `newMoved` (added lines), `oldMovedDimmed`, `oldMovedAlternative`,
+       `oldMovedAlternativeDimmed`, `newMovedDimmed`, `newMovedAlternative`
+       and `newMovedAlternativeDimmed` (See the '<mode>'
+       setting of '--color-moved' in linkgit:git-diff[1] for details).
 
 color.decorate.<slot>::
        Use customized color for 'git log --decorate' output.  `<slot>` is one
@@ -1553,11 +1570,13 @@ gc.<pattern>.reflogExpireUnreachable::
 gc.rerereResolved::
        Records of conflicted merge you resolved earlier are
        kept for this many days when 'git rerere gc' is run.
+       You can also use more human-readable "1.month.ago", etc.
        The default is 60 days.  See linkgit:git-rerere[1].
 
 gc.rerereUnresolved::
        Records of conflicted merge you have not resolved are
        kept for this many days when 'git rerere gc' is run.
+       You can also use more human-readable "1.month.ago", etc.
        The default is 15 days.  See linkgit:git-rerere[1].
 
 gitcvs.commitMsgAnnotation::
@@ -2912,8 +2931,8 @@ sendemail.smtpsslcertpath::
 
 sendemail.<identity>.*::
        Identity-specific versions of the 'sendemail.*' parameters
-       found below, taking precedence over those when the this
-       identity is selected, through command-line or
+       found below, taking precedence over those when this
+       identity is selected, through either the command-line or
        `sendemail.identity`.
 
 sendemail.aliasesFile::
index 56dedafcd4bd68c4192fbe22469428ed274a46a0..a88c76741e781f5888fd467e17d02c633c87f15d 100644 (file)
@@ -231,6 +231,40 @@ ifdef::git-diff[]
 endif::git-diff[]
        It is the same as `--color=never`.
 
+--color-moved[=<mode>]::
+       Moved lines of code are colored differently.
+ifdef::git-diff[]
+       It can be changed by the `diff.colorMoved` configuration setting.
+endif::git-diff[]
+       The <mode> defaults to 'no' if the option is not given
+       and to 'zebra' if the option with no mode is given.
+       The mode must be one of:
++
+--
+no::
+       Moved lines are not highlighted.
+default::
+       Is a synonym for `zebra`. This may change to a more sensible mode
+       in the future.
+plain::
+       Any line that is added in one location and was removed
+       in another location will be colored with 'color.diff.newMoved'.
+       Similarly 'color.diff.oldMoved' will be used for removed lines
+       that are added somewhere else in the diff. This mode picks up any
+       moved line, but it is not very useful in a review to determine
+       if a block of code was moved without permutation.
+zebra::
+       Blocks of moved text of at least 20 alphanumeric characters
+       are detected greedily. The detected blocks are
+       painted using either the 'color.diff.{old,new}Moved' color or
+       'color.diff.{old,new}MovedAlternative'. The change between
+       the two colors indicates that a new block was detected.
+dimmed_zebra::
+       Similar to 'zebra', but additional dimming of uninteresting parts
+       of moved code is performed. The bordering lines of two adjacent
+       blocks are considered interesting, the rest is uninteresting.
+--
+
 --word-diff[=<mode>]::
        Show a word diff, using the <mode> to delimit changed words.
        By default, words are delimited by whitespace; see
index 631cbd840a08eb29add1e900ac9272992669a75c..4ebc3d32719dfefa988d34b41871f0e9fb969471 100644 (file)
@@ -66,7 +66,7 @@ OPTIONS
        disables it is in effect), make sure the patch is
        applicable to what the current index file records.  If
        the file to be patched in the working tree is not
-       up-to-date, it is flagged as an error.  This flag also
+       up to date, it is flagged as an error.  This flag also
        causes the index file to be updated.
 
 --cached::
@@ -259,7 +259,7 @@ treats these changes as follows.
 If `--index` is specified (explicitly or implicitly), then the submodule
 commits must match the index exactly for the patch to apply.  If any
 of the submodules are checked-out, then these check-outs are completely
-ignored, i.e., they are not required to be up-to-date or clean and they
+ignored, i.e., they are not required to be up to date or clean and they
 are not updated.
 
 If `--index` is not specified, then the submodule commits in the patch
index d0b33587717a198655164f5dd5e35e116a8a30e0..e292737b9c5ab65d53ed3caf2e553a7e7e098fb5 100644 (file)
@@ -195,10 +195,8 @@ start-point is either a local or remote-tracking branch.
        branch.autoSetupMerge configuration variable is true.
 
 --set-upstream::
-       If specified branch does not exist yet or if `--force` has been
-       given, acts exactly like `--track`. Otherwise sets up configuration
-       like `--track` would when creating the branch, except that where
-       branch points to is not changed.
+       As this option had confusing syntax, it is no longer supported.
+       Please use `--track` or `--set-upstream-to` instead.
 
 -u <upstream>::
 --set-upstream-to=<upstream>::
index a336ae5f6fd5587aa8f778d2378783d0fe2e6dc4..ba90066f108cf525eb7781c1607d65358c617b0e 100644 (file)
@@ -223,7 +223,7 @@ access method and requested operation.
 That means that even if you offer only read access (e.g. by using
 the pserver method), 'git-cvsserver' should have write access to
 the database to work reliably (otherwise you need to make sure
-that the database is up-to-date any time 'git-cvsserver' is executed).
+that the database is up to date any time 'git-cvsserver' is executed).
 
 By default it uses SQLite databases in the Git directory, named
 `gitcvs.<module_name>.sqlite`. Note that the SQLite backend creates
index a1715069529eba9d143d527c058e962381efd743..b380677718ae129b4643814d7c1a16351ce4d779 100644 (file)
@@ -85,7 +85,7 @@ a 'git write-tree' + 'git diff-tree'. Thus that's the default mode.
 The non-cached version asks the question:
 
   show me the differences between HEAD and the currently checked out
-  tree - index contents _and_ files that aren't up-to-date
+  tree - index contents _and_ files that aren't up to date
 
 which is obviously a very useful question too, since that tells you what
 you *could* commit. Again, the output matches the 'git diff-tree -r'
@@ -100,8 +100,8 @@ have not actually done a 'git update-index' on it yet - there is no
   torvalds@ppc970:~/v2.6/linux> git diff-index --abbrev HEAD
   :100644 100664 7476bb... 000000...      kernel/sched.c
 
-i.e., it shows that the tree has changed, and that `kernel/sched.c` has is
-not up-to-date and may contain new stuff. The all-zero sha1 means that to
+i.e., it shows that the tree has changed, and that `kernel/sched.c` is
+not up to date and may contain new stuff. The all-zero sha1 means that to
 get the real diff, you need to look at the object in the working directory
 directly rather than do an object-to-object diff.
 
index 31cdeaecdfde8fb358c35c3a9a2e3c4279d440d0..9dd19a1dd9f126fbf54f1dc628b30fbe05445cf3 100644 (file)
@@ -3,24 +3,27 @@ git-interpret-trailers(1)
 
 NAME
 ----
-git-interpret-trailers - help add structured information into commit messages
+git-interpret-trailers - add or parse structured information in commit messages
 
 SYNOPSIS
 --------
 [verse]
-'git interpret-trailers' [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]
+'git interpret-trailers' [options] [(--trailer <token>[(=|:)<value>])...] [<file>...]
+'git interpret-trailers' [options] [--parse] [<file>...]
 
 DESCRIPTION
 -----------
-Help adding 'trailers' lines, that look similar to RFC 822 e-mail
+Help parsing or adding 'trailers' lines, that look similar to RFC 822 e-mail
 headers, at the end of the otherwise free-form part of a commit
 message.
 
 This command reads some patches or commit messages from either the
-<file> arguments or the standard input if no <file> is specified. Then
-this command applies the arguments passed using the `--trailer`
-option, if any, to the commit message part of each input file. The
-result is emitted on the standard output.
+<file> arguments or the standard input if no <file> is specified. If
+`--parse` is specified, the output consists of the parsed trailers.
+
+Otherwise, this command applies the arguments passed using the
+`--trailer` option, if any, to the commit message part of each input
+file. The result is emitted on the standard output.
 
 Some configuration variables control the way the `--trailer` arguments
 are applied to each commit message and the way any existing trailer in
@@ -80,6 +83,45 @@ OPTIONS
        trailer to the input messages. See the description of this
        command.
 
+--where <placement>::
+--no-where::
+       Specify where all new trailers will be added.  A setting
+       provided with '--where' overrides all configuration variables
+       and applies to all '--trailer' options until the next occurrence of
+       '--where' or '--no-where'.
+
+--if-exists <action>::
+--no-if-exists::
+       Specify what action will be performed when there is already at
+       least one trailer with the same <token> in the message.  A setting
+       provided with '--if-exists' overrides all configuration variables
+       and applies to all '--trailer' options until the next occurrence of
+       '--if-exists' or '--no-if-exists'.
+
+--if-missing <action>::
+--no-if-missing::
+       Specify what action will be performed when there is no other
+       trailer with the same <token> in the message.  A setting
+       provided with '--if-missing' overrides all configuration variables
+       and applies to all '--trailer' options until the next occurrence of
+       '--if-missing' or '--no-if-missing'.
+
+--only-trailers::
+       Output only the trailers, not any other parts of the input.
+
+--only-input::
+       Output only trailers that exist in the input; do not add any
+       from the command-line or by following configured `trailer.*`
+       rules.
+
+--unfold::
+       Remove any whitespace-continuation in trailers, so that each
+       trailer appears on a line by itself with its full content.
+
+--parse::
+       A convenience alias for `--only-trailers --only-input
+       --unfold`.
+
 CONFIGURATION VARIABLES
 -----------------------
 
@@ -170,8 +212,8 @@ trailer.<token>.where::
        configuration variable and it overrides what is specified by
        that option for trailers with the specified <token>.
 
-trailer.<token>.ifexist::
-       This option takes the same values as the 'trailer.ifexist'
+trailer.<token>.ifexists::
+       This option takes the same values as the 'trailer.ifexists'
        configuration variable and it overrides what is specified by
        that option for trailers with the specified <token>.
 
index 04fdd8cf086db6413a01421c306a80c9583f7fa4..4df6431c341a466f2cb9fef094607751b58c008d 100644 (file)
@@ -64,6 +64,14 @@ OPTIONS
 -------
 include::merge-options.txt[]
 
+--signoff::
+       Add Signed-off-by line by the committer at the end of the commit
+       log message.  The meaning of a signoff depends on the project,
+       but it typically certifies that committer has
+       the rights to submit this work under the same license and
+       agrees to a Developer Certificate of Origin
+       (see http://developercertificate.org/ for more information).
+
 -S[<keyid>]::
 --gpg-sign[=<keyid>]::
        GPG-sign the resulting merge commit. The `keyid` argument is
@@ -133,7 +141,7 @@ exception is when the changed index entries are in the state that
 would result from the merge already.)
 
 If all named commits are already ancestors of `HEAD`, 'git merge'
-will exit early with the message "Already up-to-date."
+will exit early with the message "Already up to date."
 
 FAST-FORWARD MERGE
 ------------------
@@ -280,7 +288,10 @@ After seeing a conflict, you can do two things:
 
  * Resolve the conflicts.  Git will mark the conflicts in
    the working tree.  Edit the files into shape and
-   'git add' them to the index.  Use 'git commit' to seal the deal.
+   'git add' them to the index.  Use 'git commit' or
+   'git merge --continue' to seal the deal. The latter command
+   checks whether there is a (interrupted) merge in progress
+   before calling 'git commit'.
 
 You can work through the conflict with a number of tools:
 
index 8973510a41c1e31319dad85d4987b3f0dfd7d8ca..473a16135abf867a77af26b35016dab0a2b6954d 100644 (file)
@@ -18,8 +18,9 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Reads list of objects from the standard input, and writes a packed
-archive with specified base-name, or to the standard output.
+Reads list of objects from the standard input, and writes either one or
+more packed archives with the specified base-name to disk, or a packed
+archive to the standard output.
 
 A packed archive is an efficient way to transfer a set of objects
 between two repositories as well as an access efficient archival
@@ -47,9 +48,9 @@ transport by their peers.
 OPTIONS
 -------
 base-name::
-       Write into a pair of files (.pack and .idx), using
+       Write into pairs of files (.pack and .idx), using
        <base-name> to determine the name of the created file.
-       When this option is used, the two files are written in
+       When this option is used, the two files in a pair are written in
        <base-name>-<SHA-1>.{pack,idx} files.  <SHA-1> is a hash
        based on the pack content and is written to the standard
        output of the command.
@@ -108,9 +109,13 @@ base-name::
        is taken from the `pack.windowMemory` configuration variable.
 
 --max-pack-size=<n>::
-       Maximum size of each output pack file. The size can be suffixed with
+       In unusual scenarios, you may not be able to create files
+       larger than a certain size on your filesystem, and this option
+       can be used to tell the command to split the output packfile
+       into multiple independent packfiles, each not larger than the
+       given size. The size can be suffixed with
        "k", "m", or "g". The minimum size allowed is limited to 1 MiB.
-       If specified, multiple packfiles may be created, which also
+       This option
        prevents the creation of a bitmap index.
        The default is unlimited, unless the config variable
        `pack.packSizeLimit` is set.
index 4f6bed61a92b74e4540b71e0e0cac7cba60302ca..6805a74aec20cacd7b9320136b8648ad5b40ef5b 100644 (file)
@@ -334,7 +334,7 @@ which makes little sense.
 
 -f::
 --force-rebase::
-       Force a rebase even if the current branch is up-to-date and
+       Force a rebase even if the current branch is up to date and
        the command without `--force` would return without doing anything.
 +
 You may find this (or --no-ff with an interactive rebase) helpful after
index 9ee083c415a27dc499b75031d49df9e3f3856ec0..031f31fa471154f474b35448564aff6c8c2b3f51 100644 (file)
@@ -205,7 +205,7 @@ development on the topic branch:
 ------------
 
 you could run `git rebase master topic`, to bring yourself
-up-to-date before your topic is ready to be sent upstream.
+up to date before your topic is ready to be sent upstream.
 This would result in falling back to a three-way merge, and it
 would conflict the same way as the test merge you resolved earlier.
 'git rerere' will be run by 'git rebase' to help you resolve this
index 683e59133003536c776c476304e369bdd80e27a2..b5c46223c44b1b17673ae4782f74528b3d151bed 100644 (file)
@@ -146,7 +146,7 @@ the submodule's history. If it exists the submodule.<name> section
 in the linkgit:gitmodules[5] file will also be removed and that file
 will be staged (unless --cached or -n are used).
 
-A submodule is considered up-to-date when the HEAD is the same as
+A submodule is considered up to date when the HEAD is the same as
 recorded in the index, no tracked files are modified and no untracked
 files that aren't ignored are present in the submodules work tree.
 Ignored files are deemed expendable and won't stop a submodule's work
index aa2aeabb60209fabd772907a91c2812b2a305d7d..636e09048e8846b813166737ef58eef96eb3a3c4 100644 (file)
@@ -424,7 +424,7 @@ Any other arguments are passed directly to 'git log'
 'set-tree'::
        You should consider using 'dcommit' instead of this command.
        Commit specified commit or tree objects to SVN.  This relies on
-       your imported fetch data being up-to-date.  This makes
+       your imported fetch data being up to date.  This makes
        absolutely no attempts to do patching when committing to SVN, it
        simply overwrites files with those specified in the tree or
        commit.  All merging is assumed to have taken place
index 1579abf3c3b45f4e63f8f17746dde31a06a3a564..e19eba62cda292cc51407cbcd7e94675560f97f2 100644 (file)
@@ -214,7 +214,7 @@ will remove the intended effect of the option.
 Using --refresh
 ---------------
 `--refresh` does not calculate a new sha1 file or bring the index
-up-to-date for mode/content changes. But what it *does* do is to
+up to date for mode/content changes. But what it *does* do is to
 "re-match" the stat information of a file with the index, so that you
 can refresh the index for a file that hasn't been changed but where
 the stat entry is out of date.
index 7577f27ec20c90eabea2652a438ca09961559f6a..e29a9effccbcf439fec8a8fe9bfd1927f58439d7 100644 (file)
@@ -631,7 +631,7 @@ So after you do a `cp -a` to create a new copy, you'll want to do
 $ git update-index --refresh
 ----------------
 +
-in the new repository to make sure that the index file is up-to-date.
+in the new repository to make sure that the index file is up to date.
 
 Note that the second point is true even across machines. You can
 duplicate a remote Git repository with *any* regular copy mechanism, be it
@@ -701,7 +701,7 @@ $ git checkout-index -u -a
 ----------------
 
 where the `-u` flag means that you want the checkout to keep the index
-up-to-date (so that you don't have to refresh it afterward), and the
+up to date (so that you don't have to refresh it afterward), and the
 `-a` flag means "check out all files" (if you have a stale copy or an
 older version of a checked out tree you may also need to add the `-f`
 flag first, to tell 'git checkout-index' to *force* overwriting of any old
@@ -1283,7 +1283,7 @@ run a single command, 'git-receive-pack'.
 
 First, you need to create an empty repository on the remote
 machine that will house your public repository. This empty
-repository will be populated and be kept up-to-date by pushing
+repository will be populated and be kept up to date by pushing
 into it later. Obviously, this repository creation needs to be
 done only once.
 
@@ -1450,7 +1450,7 @@ transport protocols (HTTP), you need to keep this repository
 would contain a call to 'git update-server-info'
 but you need to manually enable the hook with
 `mv post-update.sample post-update`.  This makes sure
-'git update-server-info' keeps the necessary files up-to-date.
+'git update-server-info' keeps the necessary files up to date.
 
 3. Push into the public repository from your primary
    repository.
index 623ed1a13829188971531aa62c6e109e9d29ec44..1bb4f92d4d317e16929767066a61c123fee0f280 100644 (file)
@@ -369,7 +369,7 @@ them.
 
 When enabled, the default 'post-update' hook runs
 'git update-server-info' to keep the information used by dumb
-transports (e.g., HTTP) up-to-date.  If you are publishing
+transports (e.g., HTTP) up to date.  If you are publishing
 a Git repository that is accessible via HTTP, you should
 probably enable this hook.
 
index f51ed4e37cd3fad80bf855e6217906f094f894fb..adf9554ad2f84cb69a6b0a940e4d3e0e336df9ff 100644 (file)
@@ -71,7 +71,7 @@ objects/info/packs::
        This file is to help dumb transports discover what packs
        are available in this object store.  Whenever a pack is
        added or removed, `git update-server-info` should be run
-       to keep this file up-to-date if the repository is
+       to keep this file up to date if the repository is
        published for dumb transports.  'git repack' does this
        by default.
 
index 794b83393ea21a8d569c34791ee4af1dfb99a57a..242de31cb6ccc6600b94a68a9efd215df2cb71f7 100644 (file)
@@ -109,7 +109,7 @@ summary of the situation with 'git status':
 $ git status
 On branch master
 Changes to be committed:
-Your branch is up-to-date with 'origin/master'.
+Your branch is up to date with 'origin/master'.
   (use "git reset HEAD <file>..." to unstage)
 
        modified:   file1
index 5b4a62e93624bc8289c835631aab5d5f13dff708..4e323043017deee83d547e52e7797dd61a02cd5d 100644 (file)
@@ -39,7 +39,7 @@ set to `no` at the beginning of them.
 
 --ff-only::
        Refuse to merge and exit with a non-zero status unless the
-       current `HEAD` is already up-to-date or the merge can be
+       current `HEAD` is already up to date or the merge can be
        resolved as a fast-forward.
 
 --log[=<n>]::
index 973d19606b63c314786de4965160b2c285987947..d433d50f8104dd5c86e49c844047208fe455b6a0 100644 (file)
@@ -205,7 +205,10 @@ endif::git-rev-list[]
 - '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'
   respectively, but padding both sides (i.e. the text is centered)
 - %(trailers): display the trailers of the body as interpreted by
-  linkgit:git-interpret-trailers[1]
+  linkgit:git-interpret-trailers[1]. If the `:only` option is given,
+  omit non-trailer lines from the trailer block.  If the `:unfold`
+  option is given, behave as if interpret-trailer's `--unfold` option
+  was given. E.g., `%(trailers:only:unfold)` to do both.
 
 NOTE: Some placeholders may depend on other options given to the
 revision traversal engine. For example, the `%g*` reflog options will
index a6cf9eb380fabedd0e89b84d182e6e2c4798e503..7d860bfca1442e7957c41b7a7a5cd6f82141ed7e 100644 (file)
@@ -184,6 +184,14 @@ explicitly.
        Pretend as if all objects mentioned by reflogs are listed on the
        command line as `<commit>`.
 
+--single-worktree::
+       By default, all working trees will be examined by the
+       following options when there are more than one (see
+       linkgit:git-worktree[1]): `--all`, `--reflog` and
+       `--indexed-objects`.
+       This option forces them to examine the current working tree
+       only.
+
 --ignore-missing::
        Upon seeing an invalid object name in the input, pretend as if
        the bad input was not given.
index 37379d83376fc2342d1a81570b3de86b8e7dcd49..46c3d5c3550a1ba7c83b6937fe68538c3c12074e 100644 (file)
@@ -32,11 +32,8 @@ Iteration functions
 
 * `for_each_glob_ref_in()` the previous and `for_each_ref_in()` combined.
 
-* `head_ref_submodule()`, `for_each_ref_submodule()`,
-  `for_each_ref_in_submodule()`, `for_each_tag_ref_submodule()`,
-  `for_each_branch_ref_submodule()`, `for_each_remote_ref_submodule()`
-  do the same as the functions described above but for a specified
-  submodule.
+* Use `refs_` API for accessing submodules. The submodule ref store could
+  be obtained with `get_submodule_ref_store()`.
 
 * `for_each_rawref()` can be used to learn about broken ref and symref.
 
index 14af37c3f14854e87d9d28f60b5ff72eb189c37f..bde18622a87404fc258c60ecb87c43bfeb047f0c 100644 (file)
@@ -55,9 +55,9 @@ Initializing
 
 `fill_tree_descriptor`::
 
-       Initialize a `tree_desc` and decode its first entry given the sha1 of
-       a tree. Returns the `buffer` member if the sha1 is a valid tree
-       identifier and NULL otherwise.
+       Initialize a `tree_desc` and decode its first entry given the
+       object ID of a tree. Returns the `buffer` member if the latter
+       is a valid tree identifier and NULL otherwise.
 
 `setup_traverse_info`::
 
index a34917153fd3dc10fefe71a1ba05e5dacb9af7f9..ed1eae8b83a651d34f9ce2488f58d02a763fbfd4 100644 (file)
@@ -199,7 +199,7 @@ After reference and capabilities discovery, the client can decide to
 terminate the connection by sending a flush-pkt, telling the server it can
 now gracefully terminate, and disconnect, when it does not need any pack
 data. This can happen with the ls-remote command, and also can happen when
-the client already is up-to-date.
+the client already is up to date.
 
 Otherwise, it enters the negotiation phase, where the client and
 server determine what the minimal packfile necessary for transport is,
index c79d4a7c476da2c57c117529150de77646ed116e..1f1c33d0da30dbdf90c37bd808eb52059b598c84 100644 (file)
@@ -32,7 +32,7 @@ or the result.
 If multiple cases apply, the one used is listed first.
 
 A result which changes the index is an error if the index is not empty
-and not up-to-date.
+and not up to date.
 
 Entries marked '+' have stat information. Spaces marked '*' don't
 affect the result.
@@ -65,7 +65,7 @@ empty, no entry is left for that stage). Otherwise, the given entry is
 left in stage 0, and there are no other entries.
 
 A result of "no merge" is an error if the index is not empty and not
-up-to-date.
+up to date.
 
 *empty* means that the tree must not have a directory-file conflict
  with the entry.
index bc29298678a6d2d9f6b2927f97c40ba8978bd93b..b4d88af133e8eafd92f86443bd74de063bad4835 100644 (file)
@@ -2044,10 +2044,12 @@ If a push would not result in a <<fast-forwards,fast-forward>> of the
 remote branch, then it will fail with an error like:
 
 -------------------------------------------------
-error: remote 'refs/heads/master' is not an ancestor of
- local  'refs/heads/master'.
- Maybe you are not up-to-date and need to pull first?
-error: failed to push to 'ssh://yourserver.com/~you/proj.git'
+ ! [rejected]        master -> master (non-fast-forward)
+error: failed to push some refs to '...'
+hint: Updates were rejected because the tip of your current branch is behind
+hint: its remote counterpart. Integrate the remote changes (e.g.
+hint: 'git pull ...') before pushing again.
+hint: See the 'Note about fast-forwards' in 'git push --help' for details.
 -------------------------------------------------
 
 This can happen, for example, if you:
@@ -2193,7 +2195,7 @@ $ cd work
 Linus's tree will be stored in the remote-tracking branch named origin/master,
 and can be updated using linkgit:git-fetch[1]; you can track other
 public trees using linkgit:git-remote[1] to set up a "remote" and
-linkgit:git-fetch[1] to keep them up-to-date; see
+linkgit:git-fetch[1] to keep them up to date; see
 <<repositories-and-branches>>.
 
 Now create the branches in which you are going to work; these start out
index 79550f6ddedbc64aeb1c2f72c65e4a69095deaec..68948dfbf3f29531d41be591daa9a8ba8df72708 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -162,6 +162,11 @@ all::
 # algorithm. This is slower, but may detect attempted collision attacks.
 # Takes priority over other *_SHA1 knobs.
 #
+# Define DC_SHA1_EXTERNAL in addition to DC_SHA1 if you want to build / link
+# git with the external SHA1 collision-detect library.
+# Without this option, i.e. the default behavior is to build git with its
+# own built-in code (or submodule).
+#
 # Define DC_SHA1_SUBMODULE in addition to DC_SHA1 to use the
 # sha1collisiondetection shipped as a submodule instead of the
 # non-submodule copy in sha1dc/. This is an experimental option used
@@ -655,6 +660,7 @@ TEST_PROGRAMS_NEED_X += test-parse-options
 TEST_PROGRAMS_NEED_X += test-path-utils
 TEST_PROGRAMS_NEED_X += test-prio-queue
 TEST_PROGRAMS_NEED_X += test-read-cache
+TEST_PROGRAMS_NEED_X += test-write-cache
 TEST_PROGRAMS_NEED_X += test-ref-store
 TEST_PROGRAMS_NEED_X += test-regex
 TEST_PROGRAMS_NEED_X += test-revision-walking
@@ -1474,6 +1480,15 @@ ifdef APPLE_COMMON_CRYPTO
        BASIC_CFLAGS += -DSHA1_APPLE
 else
        DC_SHA1 := YesPlease
+       BASIC_CFLAGS += -DSHA1_DC
+       LIB_OBJS += sha1dc_git.o
+ifdef DC_SHA1_EXTERNAL
+       ifdef DC_SHA1_SUBMODULE
+$(error Only set DC_SHA1_EXTERNAL or DC_SHA1_SUBMODULE, not both)
+       endif
+       BASIC_CFLAGS += -DDC_SHA1_EXTERNAL
+       EXTLIBS += -lsha1detectcoll
+else
 ifdef DC_SHA1_SUBMODULE
        LIB_OBJS += sha1collisiondetection/lib/sha1.o
        LIB_OBJS += sha1collisiondetection/lib/ubc_check.o
@@ -1483,17 +1498,15 @@ else
        LIB_OBJS += sha1dc/ubc_check.o
 endif
        BASIC_CFLAGS += \
-               -DSHA1_DC \
                -DSHA1DC_NO_STANDARD_INCLUDES \
                -DSHA1DC_INIT_SAFE_HASH_DEFAULT=0 \
                -DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"cache.h\"" \
-               -DSHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C="\"sha1dc_git.c\"" \
-               -DSHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H="\"sha1dc_git.h\"" \
                -DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="\"git-compat-util.h\""
 endif
 endif
 endif
 endif
+endif
 
 ifdef SHA1_MAX_BLOCK_SIZE
        LIB_OBJS += compat/sha1-chunked.o
@@ -2039,7 +2052,6 @@ XDIFF_OBJS += xdiff/xhistogram.o
 
 VCSSVN_OBJS += vcs-svn/line_buffer.o
 VCSSVN_OBJS += vcs-svn/sliding_window.o
-VCSSVN_OBJS += vcs-svn/repo_tree.o
 VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/svndiff.o
 VCSSVN_OBJS += vcs-svn/svndump.o
diff --git a/apply.c b/apply.c
index 956f56a9272c901ebd9087fcb6f1f9bea09b0782..71cbbd141c73bd72778d71e1e2400413c11f492f 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -219,6 +219,7 @@ struct patch {
        unsigned int recount:1;
        unsigned int conflicted_threeway:1;
        unsigned int direct_to_threeway:1;
+       unsigned int crlf_in_old:1;
        struct fragment *fragments;
        char *result;
        size_t resultsize;
@@ -811,16 +812,13 @@ static int has_epoch_timestamp(const char *nameline)
         * 1970-01-01, and the seconds part must be "00".
         */
        const char stamp_regexp[] =
-               "^(1969-12-31|1970-01-01)"
-               " "
-               "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
+               "^[0-2][0-9]:([0-5][0-9]):00(\\.0+)?"
                " "
                "([-+][0-2][0-9]:?[0-5][0-9])\n";
        const char *timestamp = NULL, *cp, *colon;
        static regex_t *stamp;
        regmatch_t m[10];
-       int zoneoffset;
-       int hourminute;
+       int zoneoffset, epoch_hour, hour, minute;
        int status;
 
        for (cp = nameline; *cp != '\n'; cp++) {
@@ -829,6 +827,18 @@ static int has_epoch_timestamp(const char *nameline)
        }
        if (!timestamp)
                return 0;
+
+       /*
+        * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
+        * (west of GMT) or 1970-01-01 (east of GMT)
+        */
+       if (skip_prefix(timestamp, "1969-12-31 ", &timestamp))
+               epoch_hour = 24;
+       else if (skip_prefix(timestamp, "1970-01-01 ", &timestamp))
+               epoch_hour = 0;
+       else
+               return 0;
+
        if (!stamp) {
                stamp = xmalloc(sizeof(*stamp));
                if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
@@ -846,6 +856,9 @@ static int has_epoch_timestamp(const char *nameline)
                return 0;
        }
 
+       hour = strtol(timestamp, NULL, 10);
+       minute = strtol(timestamp + m[1].rm_so, NULL, 10);
+
        zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10);
        if (*colon == ':')
                zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10);
@@ -854,20 +867,7 @@ static int has_epoch_timestamp(const char *nameline)
        if (timestamp[m[3].rm_so] == '-')
                zoneoffset = -zoneoffset;
 
-       /*
-        * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
-        * (west of GMT) or 1970-01-01 (east of GMT)
-        */
-       if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) ||
-           (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10)))
-               return 0;
-
-       hourminute = (strtol(timestamp + 11, NULL, 10) * 60 +
-                     strtol(timestamp + 14, NULL, 10) -
-                     zoneoffset);
-
-       return ((zoneoffset < 0 && hourminute == 1440) ||
-               (0 <= zoneoffset && !hourminute));
+       return hour * 60 + minute - zoneoffset == epoch_hour * 60;
 }
 
 /*
@@ -1661,6 +1661,19 @@ static void check_whitespace(struct apply_state *state,
        record_ws_error(state, result, line + 1, len - 2, state->linenr);
 }
 
+/*
+ * Check if the patch has context lines with CRLF or
+ * the patch wants to remove lines with CRLF.
+ */
+static void check_old_for_crlf(struct patch *patch, const char *line, int len)
+{
+       if (len >= 2 && line[len-1] == '\n' && line[len-2] == '\r') {
+               patch->ws_rule |= WS_CR_AT_EOL;
+               patch->crlf_in_old = 1;
+       }
+}
+
+
 /*
  * Parse a unified diff. Note that this really needs to parse each
  * fragment separately, since the only way to know the difference
@@ -1711,11 +1724,14 @@ static int parse_fragment(struct apply_state *state,
                        if (!deleted && !added)
                                leading++;
                        trailing++;
+                       check_old_for_crlf(patch, line, len);
                        if (!state->apply_in_reverse &&
                            state->ws_error_action == correct_ws_error)
                                check_whitespace(state, line, len, patch->ws_rule);
                        break;
                case '-':
+                       if (!state->apply_in_reverse)
+                               check_old_for_crlf(patch, line, len);
                        if (state->apply_in_reverse &&
                            state->ws_error_action != nowarn_ws_error)
                                check_whitespace(state, line, len, patch->ws_rule);
@@ -1724,6 +1740,8 @@ static int parse_fragment(struct apply_state *state,
                        trailing = 0;
                        break;
                case '+':
+                       if (state->apply_in_reverse)
+                               check_old_for_crlf(patch, line, len);
                        if (!state->apply_in_reverse &&
                            state->ws_error_action != nowarn_ws_error)
                                check_whitespace(state, line, len, patch->ws_rule);
@@ -2266,8 +2284,11 @@ static void show_stats(struct apply_state *state, struct patch *patch)
                add, pluses, del, minuses);
 }
 
-static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
+static int read_old_data(struct stat *st, struct patch *patch,
+                        const char *path, struct strbuf *buf)
 {
+       enum safe_crlf safe_crlf = patch->crlf_in_old ?
+               SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
                if (strbuf_readlink(buf, path, st->st_size) < 0)
@@ -2276,7 +2297,15 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
        case S_IFREG:
                if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
                        return error(_("unable to open or read %s"), path);
-               convert_to_git(&the_index, path, buf->buf, buf->len, buf, 0);
+               /*
+                * "git apply" without "--index/--cached" should never look
+                * at the index; the target file may not have been added to
+                * the index yet, and we may not even be in any Git repository.
+                * Pass NULL to convert_to_git() to stress this; the function
+                * should never look at the index when explicit crlf option
+                * is given.
+                */
+               convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
                return 0;
        default:
                return -1;
@@ -3379,6 +3408,7 @@ static int load_patch_target(struct apply_state *state,
                             struct strbuf *buf,
                             const struct cache_entry *ce,
                             struct stat *st,
+                            struct patch *patch,
                             const char *name,
                             unsigned expected_mode)
 {
@@ -3394,7 +3424,7 @@ static int load_patch_target(struct apply_state *state,
                } else if (has_symlink_leading_path(name, strlen(name))) {
                        return error(_("reading from '%s' beyond a symbolic link"), name);
                } else {
-                       if (read_old_data(st, name, buf))
+                       if (read_old_data(st, patch, name, buf))
                                return error(_("failed to read %s"), name);
                }
        }
@@ -3427,7 +3457,7 @@ static int load_preimage(struct apply_state *state,
                /* We have a patched copy in memory; use that. */
                strbuf_add(&buf, previous->result, previous->resultsize);
        } else {
-               status = load_patch_target(state, &buf, ce, st,
+               status = load_patch_target(state, &buf, ce, st, patch,
                                           patch->old_name, patch->old_mode);
                if (status < 0)
                        return status;
@@ -3515,7 +3545,7 @@ static int load_current(struct apply_state *state,
        if (verify_index_match(ce, &st))
                return error(_("%s: does not match index"), name);
 
-       status = load_patch_target(state, &buf, ce, &st, name, mode);
+       status = load_patch_target(state, &buf, ce, &st, patch, name, mode);
        if (status < 0)
                return status;
        else if (status)
index 557dd2db85ff261ee00ec50ea0bfe286bff7bda3..1ab8d3a1d7cc9c3e55465fbadf899e8a267de689 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -103,17 +103,39 @@ struct archiver_context {
        struct directory *bottom;
 };
 
+static const struct attr_check *get_archive_attrs(const char *path)
+{
+       static struct attr_check *check;
+       if (!check)
+               check = attr_check_initl("export-ignore", "export-subst", NULL);
+       return git_check_attr(path, check) ? NULL : check;
+}
+
+static int check_attr_export_ignore(const struct attr_check *check)
+{
+       return check && ATTR_TRUE(check->items[0].value);
+}
+
+static int check_attr_export_subst(const struct attr_check *check)
+{
+       return check && ATTR_TRUE(check->items[1].value);
+}
+
+static int should_queue_directories(const struct archiver_args *args)
+{
+       return args->pathspec.has_wildcard;
+}
+
 static int write_archive_entry(const unsigned char *sha1, const char *base,
                int baselen, const char *filename, unsigned mode, int stage,
                void *context)
 {
        static struct strbuf path = STRBUF_INIT;
-       static struct attr_check *check;
        struct archiver_context *c = context;
        struct archiver_args *args = c->args;
        write_archive_entry_fn_t write_entry = c->write_entry;
-       const char *path_without_prefix;
        int err;
+       const char *path_without_prefix;
 
        args->convert = 0;
        strbuf_reset(&path);
@@ -125,12 +147,12 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
                strbuf_addch(&path, '/');
        path_without_prefix = path.buf + args->baselen;
 
-       if (!check)
-               check = attr_check_initl("export-ignore", "export-subst", NULL);
-       if (!git_check_attr(path_without_prefix, check)) {
-               if (ATTR_TRUE(check->items[0].value))
+       if (!S_ISDIR(mode) || !should_queue_directories(args)) {
+               const struct attr_check *check;
+               check = get_archive_attrs(path_without_prefix);
+               if (check_attr_export_ignore(check))
                        return 0;
-               args->convert = ATTR_TRUE(check->items[1].value);
+               args->convert = check_attr_export_subst(check);
        }
 
        if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
@@ -204,6 +226,17 @@ static int queue_or_write_archive_entry(const unsigned char *sha1,
        }
 
        if (S_ISDIR(mode)) {
+               size_t baselen = base->len;
+               const struct attr_check *check;
+
+               /* Borrow base, but restore its original value when done. */
+               strbuf_addstr(base, filename);
+               strbuf_addch(base, '/');
+               check = get_archive_attrs(base->buf);
+               strbuf_setlen(base, baselen);
+
+               if (check_attr_export_ignore(check))
+                       return 0;
                queue_directory(sha1, base, filename,
                                mode, stage, c);
                return READ_TREE_RECURSIVE;
@@ -257,7 +290,7 @@ int write_archive_entries(struct archiver_args *args,
        }
 
        err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
-                                 args->pathspec.has_wildcard ?
+                                 should_queue_directories(args) ?
                                  queue_or_write_archive_entry :
                                  write_archive_entry_buf,
                                  &context);
index 36541d05cd7b934bceba1af4fd4c121e51cd862b..703ded69ca2697f34bc1e225ccecebf33e5ad0b1 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -90,24 +90,24 @@ int install_branch_config(int flag, const char *local, const char *origin, const
                if (shortname) {
                        if (origin)
                                printf_ln(rebasing ?
-                                         _("Branch %s set up to track remote branch %s from %s by rebasing.") :
-                                         _("Branch %s set up to track remote branch %s from %s."),
+                                         _("Branch '%s' set up to track remote branch '%s' from '%s' by rebasing.") :
+                                         _("Branch '%s' set up to track remote branch '%s' from '%s'."),
                                          local, shortname, origin);
                        else
                                printf_ln(rebasing ?
-                                         _("Branch %s set up to track local branch %s by rebasing.") :
-                                         _("Branch %s set up to track local branch %s."),
+                                         _("Branch '%s' set up to track local branch '%s' by rebasing.") :
+                                         _("Branch '%s' set up to track local branch '%s'."),
                                          local, shortname);
                } else {
                        if (origin)
                                printf_ln(rebasing ?
-                                         _("Branch %s set up to track remote ref %s by rebasing.") :
-                                         _("Branch %s set up to track remote ref %s."),
+                                         _("Branch '%s' set up to track remote ref '%s' by rebasing.") :
+                                         _("Branch '%s' set up to track remote ref '%s'."),
                                          local, remote);
                        else
                                printf_ln(rebasing ?
-                                         _("Branch %s set up to track local ref %s by rebasing.") :
-                                         _("Branch %s set up to track local ref %s."),
+                                         _("Branch '%s' set up to track local ref '%s' by rebasing.") :
+                                         _("Branch '%s' set up to track local ref '%s'."),
                                          local, remote);
                }
        }
@@ -357,8 +357,9 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref,
 
                if (worktrees[i]->is_detached)
                        continue;
-               if (worktrees[i]->head_ref &&
-                   strcmp(oldref, worktrees[i]->head_ref))
+               if (!worktrees[i]->head_ref)
+                       continue;
+               if (strcmp(oldref, worktrees[i]->head_ref))
                        continue;
 
                refs = get_worktree_ref_store(worktrees[i]);
index 5d5773d5cd2fc9e713498a72da7b57b84fd25d19..c20548e4f545116c6e3bff75e59e953be603cc2a 100644 (file)
@@ -116,6 +116,7 @@ int add_files_to_cache(const char *prefix,
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
        rev.diffopt.format_callback_data = &data;
+       rev.diffopt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
        rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
        return !!data.add_errors;
index bda1a787265e6d44d2ec0bec1e4dee5bf8de9c3b..67adaef4d80ee4e2fcd28755255935f0fcae6c03 100644 (file)
@@ -488,7 +488,7 @@ static int read_ancestry(const char *graft_file)
                return -1;
        while (!strbuf_getwholeline(&buf, fp, '\n')) {
                /* The format is just "Commit Parent1 Parent2 ...\n" */
-               struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
+               struct commit_graft *graft = read_graft_line(&buf);
                if (graft)
                        register_commit_graft(graft, 0);
        }
@@ -925,8 +925,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        sb.found_guilty_entry = &found_guilty_entry;
        sb.found_guilty_entry_data = &pi;
        if (show_progress)
-               pi.progress = start_progress_delay(_("Blaming lines"),
-                                                  sb.num_lines, 50, 1);
+               pi.progress = start_delayed_progress(_("Blaming lines"), sb.num_lines);
 
        assign_blame(&sb, opt);
 
index 16d391b407c9fa688b48a3e28f77800dcc1e2e0c..355f9ef5da164d4d3e88872984fced9e9a744a84 100644 (file)
@@ -561,8 +561,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT__QUIET(&quiet, N_("suppress informational messages")),
                OPT_SET_INT('t', "track",  &track, N_("set up tracking mode (see git-pull(1))"),
                        BRANCH_TRACK_EXPLICIT),
-               OPT_SET_INT( 0, "set-upstream",  &track, N_("change upstream info"),
-                       BRANCH_TRACK_OVERRIDE),
+               { OPTION_SET_INT, 0, "set-upstream", &track, NULL, N_("do not use"),
+                       PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, BRANCH_TRACK_OVERRIDE },
                OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
                OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("Unset the upstream info")),
                OPT__COLOR(&branch_use_color, N_("use colored output")),
@@ -759,8 +759,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                strbuf_release(&buf);
        } else if (argc > 0 && argc <= 2) {
                struct branch *branch = branch_get(argv[0]);
-               int branch_existed = 0, remote_tracking = 0;
-               struct strbuf buf = STRBUF_INIT;
 
                if (!strcmp(argv[0], "HEAD"))
                        die(_("it does not make sense to create 'HEAD' manually"));
@@ -772,28 +770,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
 
                if (track == BRANCH_TRACK_OVERRIDE)
-                       fprintf(stderr, _("The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to\n"));
-
-               strbuf_addf(&buf, "refs/remotes/%s", branch->name);
-               remote_tracking = ref_exists(buf.buf);
-               strbuf_release(&buf);
+                       die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
 
-               branch_existed = ref_exists(branch->refname);
                create_branch(argv[0], (argc == 2) ? argv[1] : head,
                              force, reflog, 0, quiet, track);
 
-               /*
-                * We only show the instructions if the user gave us
-                * one branch which doesn't exist locally, but is the
-                * name of a remote-tracking branch.
-                */
-               if (argc == 1 && track == BRANCH_TRACK_OVERRIDE &&
-                   !branch_existed && remote_tracking) {
-                       fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name);
-                       fprintf(stderr, "    git branch -d %s\n", branch->name);
-                       fprintf(stderr, "    git branch --set-upstream-to %s\n", branch->name);
-               }
-
        } else
                usage_with_options(builtin_branch_usage, options);
 
index 2d75ac66c7f8447e846e0bc38072b76bddc3672b..5c202b7af57e26f5171eb8a83b107fceeb747488 100644 (file)
@@ -861,7 +861,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
        }
 
        if (starts_with(var, "submodule."))
-               return submodule_config(var, value, NULL);
+               return git_default_submodule_config(var, value, NULL);
 
        return git_xmerge_config(var, value, NULL);
 }
@@ -1182,7 +1182,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        opts.prefix = prefix;
        opts.show_progress = -1;
 
-       gitmodules_config();
        git_config(git_checkout_config, &opts);
 
        opts.track = BRANCH_TRACK_UNSPECIFIED;
index b79bcfd5b9331f79b660940bb4029aa96041d80e..b3b04f5dd3a94d1661e877c5019cc56ac46854ef 100644 (file)
@@ -195,7 +195,6 @@ static void determine_whence(struct wt_status *s)
 static void status_init_config(struct wt_status *s, config_fn_t fn)
 {
        wt_status_prepare(s);
-       gitmodules_config();
        git_config(fn, s);
        determine_whence(s);
        init_diff_ui_defaults();
index 17bf84d18f802d3f223e7408fee644b94878b35f..e88493ffe5d5d66461edb71832b58e8b3b4b2bc8 100644 (file)
@@ -26,7 +26,6 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        init_revisions(&rev, prefix);
-       gitmodules_config();
        rev.abbrev = 0;
        precompose_argv(argc, argv);
 
index 185e6f9b582fdcf15072038b570463a0cfb1bbf2..9d772f8f27fe722921c00ea57af1087f988856da 100644 (file)
@@ -23,7 +23,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        init_revisions(&rev, prefix);
-       gitmodules_config();
        rev.abbrev = 0;
        precompose_argv(argc, argv);
 
index 31d2cb410738d335d9f6431d6901ea1c0f8b67ff..d66499909e82e255a8d1356b606f9b901935317f 100644 (file)
@@ -110,7 +110,6 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
 
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        init_revisions(opt, prefix);
-       gitmodules_config();
        opt->abbrev = 0;
        opt->diff = 1;
        opt->disable_stdin = 1;
index 7cde6abbcf7651af8313bd3d70eb1944e72c9cb3..7e3ebcea38f1485f4c7fb5f5af6494ceb81f5cbb 100644 (file)
@@ -315,8 +315,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                        no_index = DIFF_NO_INDEX_IMPLICIT;
        }
 
-       if (!no_index)
-               gitmodules_config();
        init_diff_ui_defaults();
        git_config(git_diff_ui_config, NULL);
        precompose_argv(argc, argv);
index 8864d846f89846669a5403d3a9436aa784d47be2..b2d3ba7539d5b9cdc7ec6f910ee24510e709f76c 100644 (file)
@@ -111,7 +111,7 @@ static int use_wt_file(const char *workdir, const char *name,
                int fd = open(buf.buf, O_RDONLY);
 
                if (fd >= 0 &&
-                   !index_fd(wt_oid.hash, fd, &st, OBJ_BLOB, name, 0)) {
+                   !index_fd(&wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
                        if (is_null_oid(oid)) {
                                oidcpy(oid, &wt_oid);
                                use = 1;
index 08e094bf1237407f3de937970f2b7ea39c359f43..225c734924f14854784cbc1e310f2fb817f1f38b 100644 (file)
@@ -1361,11 +1361,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (depth || deepen_since || deepen_not.nr)
                deepen = 1;
 
-       if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
-               gitmodules_config();
-               git_config(submodule_config, NULL);
-       }
-
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
index 338f3ce20b5cd1516be8268dd642f1bb13e04be8..1e4c471b4141e6ec350c4caf58bacafb6ac07b7c 100644 (file)
@@ -180,7 +180,7 @@ static int traverse_reachable(void)
        unsigned int nr = 0;
        int result = 0;
        if (show_progress)
-               progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2);
+               progress = start_delayed_progress(_("Checking connectivity"), 0);
        while (pending.nr) {
                struct object_array_entry *entry;
                struct object *obj;
index 3c78fcb9b1a60b92a1a848ebc8aba01ec06feca9..c22787ac723c421b17032ecf0b718ff434203c86 100644 (file)
@@ -47,7 +47,7 @@ static struct argv_array prune = ARGV_ARRAY_INIT;
 static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
 static struct argv_array rerere = ARGV_ARRAY_INIT;
 
-static struct tempfile pidfile;
+static struct tempfile *pidfile;
 static struct lock_file log_lock;
 
 static struct string_list pack_garbage = STRING_LIST_INIT_DUP;
@@ -78,7 +78,7 @@ static void process_log_file(void)
                 */
                int saved_errno = errno;
                fprintf(stderr, _("Failed to fstat %s: %s"),
-                       get_tempfile_path(&log_lock.tempfile),
+                       get_tempfile_path(log_lock.tempfile),
                        strerror(saved_errno));
                fflush(stderr);
                commit_lock_file(&log_lock);
@@ -242,7 +242,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
        int fd;
        char *pidfile_path;
 
-       if (is_tempfile_active(&pidfile))
+       if (is_tempfile_active(pidfile))
                /* already locked */
                return NULL;
 
@@ -293,7 +293,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
        write_in_full(fd, sb.buf, sb.len);
        strbuf_release(&sb);
        commit_lock_file(&lock);
-       register_tempfile(&pidfile, pidfile_path);
+       pidfile = register_tempfile(pidfile_path);
        free(pidfile_path);
        return NULL;
 }
index a70d8e2fba9f4e371a069bd801d20dcfe195f485..19e23946ac4bfe53dd0f590fe26250b1f789778b 100644 (file)
@@ -1048,10 +1048,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        }
 #endif
 
-       if (recurse_submodules) {
-               gitmodules_config();
-       }
-
        if (show_in_pager && (cached || list.nr))
                die(_("--open-files-in-pager only works on the worktree"));
 
index d04baf999a94cfa6a07e74861876d6a9f1c88a6d..c532ff9320c751d1db5475add51f2c3c6a8c7146 100644 (file)
@@ -16,7 +16,7 @@
  * needs to bypass the data conversion performed by, and the type
  * limitation imposed by, index_fd() and its callees.
  */
-static int hash_literally(unsigned char *sha1, int fd, const char *type, unsigned flags)
+static int hash_literally(struct object_id *oid, int fd, const char *type, unsigned flags)
 {
        struct strbuf buf = STRBUF_INIT;
        int ret;
@@ -24,7 +24,7 @@ static int hash_literally(unsigned char *sha1, int fd, const char *type, unsigne
        if (strbuf_read(&buf, fd, 4096) < 0)
                ret = -1;
        else
-               ret = hash_sha1_file_literally(buf.buf, buf.len, type, sha1, flags);
+               ret = hash_sha1_file_literally(buf.buf, buf.len, type, oid, flags);
        strbuf_release(&buf);
        return ret;
 }
@@ -33,16 +33,16 @@ static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
                    int literally)
 {
        struct stat st;
-       unsigned char sha1[20];
+       struct object_id oid;
 
        if (fstat(fd, &st) < 0 ||
            (literally
-            ? hash_literally(sha1, fd, type, flags)
-            : index_fd(sha1, fd, &st, type_from_string(type), path, flags)))
+            ? hash_literally(&oid, fd, type, flags)
+            : index_fd(&oid, fd, &st, type_from_string(type), path, flags)))
                die((flags & HASH_WRITE_OBJECT)
                    ? "Unable to add %s to database"
                    : "Unable to hash %s", path);
-       printf("%s\n", sha1_to_hex(sha1));
+       printf("%s\n", oid_to_hex(&oid));
        maybe_flush_or_die(stdout, "hash to stdout");
 }
 
index 175f14797b101d22ead9d1008744440da66a7c1c..b742539d4de20361bb43bcb4dc33cfc9165c42fb 100644 (file)
@@ -16,34 +16,119 @@ static const char * const git_interpret_trailers_usage[] = {
        NULL
 };
 
+static enum trailer_where where;
+static enum trailer_if_exists if_exists;
+static enum trailer_if_missing if_missing;
+
+static int option_parse_where(const struct option *opt,
+                             const char *arg, int unset)
+{
+       return trailer_set_where(&where, arg);
+}
+
+static int option_parse_if_exists(const struct option *opt,
+                                 const char *arg, int unset)
+{
+       return trailer_set_if_exists(&if_exists, arg);
+}
+
+static int option_parse_if_missing(const struct option *opt,
+                                  const char *arg, int unset)
+{
+       return trailer_set_if_missing(&if_missing, arg);
+}
+
+static void new_trailers_clear(struct list_head *trailers)
+{
+       struct list_head *pos, *tmp;
+       struct new_trailer_item *item;
+
+       list_for_each_safe(pos, tmp, trailers) {
+               item = list_entry(pos, struct new_trailer_item, list);
+               list_del(pos);
+               free(item);
+       }
+}
+
+static int option_parse_trailer(const struct option *opt,
+                                  const char *arg, int unset)
+{
+       struct list_head *trailers = opt->value;
+       struct new_trailer_item *item;
+
+       if (unset) {
+               new_trailers_clear(trailers);
+               return 0;
+       }
+
+       if (!arg)
+               return -1;
+
+       item = xmalloc(sizeof(*item));
+       item->text = arg;
+       item->where = where;
+       item->if_exists = if_exists;
+       item->if_missing = if_missing;
+       list_add_tail(&item->list, trailers);
+       return 0;
+}
+
+static int parse_opt_parse(const struct option *opt, const char *arg,
+                          int unset)
+{
+       struct process_trailer_options *v = opt->value;
+       v->only_trailers = 1;
+       v->only_input = 1;
+       v->unfold = 1;
+       return 0;
+}
+
 int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
 {
-       int in_place = 0;
-       int trim_empty = 0;
-       struct string_list trailers = STRING_LIST_INIT_NODUP;
+       struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+       LIST_HEAD(trailers);
 
        struct option options[] = {
-               OPT_BOOL(0, "in-place", &in_place, N_("edit files in place")),
-               OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")),
-               OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"),
-                               N_("trailer(s) to add")),
+               OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")),
+               OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")),
+
+               OPT_CALLBACK(0, "where", NULL, N_("action"),
+                            N_("where to place the new trailer"), option_parse_where),
+               OPT_CALLBACK(0, "if-exists", NULL, N_("action"),
+                            N_("action if trailer already exists"), option_parse_if_exists),
+               OPT_CALLBACK(0, "if-missing", NULL, N_("action"),
+                            N_("action if trailer is missing"), option_parse_if_missing),
+
+               OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")),
+               OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")),
+               OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")),
+               { OPTION_CALLBACK, 0, "parse", &opts, NULL, N_("set parsing options"),
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse },
+               OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
+                               N_("trailer(s) to add"), option_parse_trailer),
                OPT_END()
        };
 
        argc = parse_options(argc, argv, prefix, options,
                             git_interpret_trailers_usage, 0);
 
+       if (opts.only_input && !list_empty(&trailers))
+               usage_msg_opt(
+                       _("--trailer with --only-input does not make sense"),
+                       git_interpret_trailers_usage,
+                       options);
+
        if (argc) {
                int i;
                for (i = 0; i < argc; i++)
-                       process_trailers(argv[i], in_place, trim_empty, &trailers);
+                       process_trailers(argv[i], &opts, &trailers);
        } else {
-               if (in_place)
+               if (opts.in_place)
                        die(_("no input file given for in-place editing"));
-               process_trailers(NULL, in_place, trim_empty, &trailers);
+               process_trailers(NULL, &opts, &trailers);
        }
 
-       string_list_clear(&trailers, 0);
+       new_trailers_clear(&trailers);
 
        return 0;
 }
index 25c0808409abce661d89896ceef490f15bb0b57e..f8cccbc96403a791ff9d4641bac1c30b621c0d5e 100644 (file)
@@ -1759,7 +1759,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        rev.add_signoff = do_signoff;
 
        if (show_progress)
-               progress = start_progress_delay(_("Generating patches"), total, 0, 2);
+               progress = start_delayed_progress(_("Generating patches"), total);
        while (0 <= --nr) {
                int shown;
                display_progress(progress, total - nr);
index c6126eae550beb75e7c90094be337efbb48cd3bc..e1339e6d17d2bf592b800d3e5a686cbc91c12331 100644 (file)
@@ -19,6 +19,7 @@
 #include "pathspec.h"
 #include "run-command.h"
 #include "submodule.h"
+#include "submodule-config.h"
 
 static int abbrev;
 static int show_deleted;
@@ -210,8 +211,6 @@ static void show_submodule(struct repository *superproject,
        if (repo_read_index(&submodule) < 0)
                die("index file corrupt");
 
-       repo_read_gitmodules(&submodule);
-
        show_files(&submodule, dir);
 
        repo_clear(&submodule);
@@ -609,9 +608,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
        if (require_work_tree && !is_inside_work_tree())
                setup_work_tree();
 
-       if (recurse_submodules)
-               repo_read_gitmodules(the_repository);
-
        if (recurse_submodules &&
            (show_stage || show_deleted || show_others || show_unmerged ||
             show_killed || show_modified || show_resolve_undo || with_tree))
index f12da292cf91b1ecb9fc785fd50bd1ef83b361e9..d01ddecf6602eabdca97a175e5c2a57bf1257865 100644 (file)
@@ -213,11 +213,11 @@ static void unresolved_directory(const struct traverse_info *info,
 
        newbase = traverse_path(info, p);
 
-#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid->hash : NULL)
-       buf0 = fill_tree_descriptor(t+0, ENTRY_SHA1(n + 0));
-       buf1 = fill_tree_descriptor(t+1, ENTRY_SHA1(n + 1));
-       buf2 = fill_tree_descriptor(t+2, ENTRY_SHA1(n + 2));
-#undef ENTRY_SHA1
+#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid : NULL)
+       buf0 = fill_tree_descriptor(t + 0, ENTRY_OID(n + 0));
+       buf1 = fill_tree_descriptor(t + 1, ENTRY_OID(n + 1));
+       buf2 = fill_tree_descriptor(t + 2, ENTRY_OID(n + 2));
+#undef ENTRY_OID
 
        merge_trees(t, newbase);
 
@@ -352,7 +352,7 @@ static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
 
        if (get_oid(rev, &oid))
                die("unknown rev %s", rev);
-       buf = fill_tree_descriptor(desc, oid.hash);
+       buf = fill_tree_descriptor(desc, &oid);
        if (!buf)
                die("%s is not a tree", rev);
        return buf;
index dfd6830602e62ec2cc0439b8e140bda6eae6e9fb..3672e38974f094831c305d509b9582c146f94236 100644 (file)
@@ -71,6 +71,7 @@ static int continue_current_merge;
 static int allow_unrelated_histories;
 static int show_progress = -1;
 static int default_to_upstream = 1;
+static int signoff;
 static const char *sign_commit;
 
 static struct strategy all_strategy[] = {
@@ -234,6 +235,7 @@ static struct option builtin_merge_options[] = {
        { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
          N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
        OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
+       OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
        OPT_END()
 };
 
@@ -757,13 +759,19 @@ N_("Please enter a commit message to explain why this merge is necessary,\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 *);
 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 (signoff)
+               append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
+       write_merge_heads(remoteheads);
        write_file_buf(git_path_merge_msg(), msg.buf, msg.len);
        if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
                            git_path_merge_msg(), "merge", NULL))
@@ -905,7 +913,7 @@ static int setup_with_upstream(const char ***argv)
        return i;
 }
 
-static void write_merge_state(struct commit_list *remoteheads)
+static void write_merge_heads(struct commit_list *remoteheads)
 {
        struct commit_list *j;
        struct strbuf buf = STRBUF_INIT;
@@ -921,8 +929,6 @@ static void write_merge_state(struct commit_list *remoteheads)
                strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
        }
        write_file_buf(git_path_merge_head(), buf.buf, buf.len);
-       strbuf_addch(&merge_msg, '\n');
-       write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
 
        strbuf_reset(&buf);
        if (fast_forward == FF_NO)
@@ -930,6 +936,13 @@ static void write_merge_state(struct commit_list *remoteheads)
        write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
 }
 
+static void write_merge_state(struct commit_list *remoteheads)
+{
+       write_merge_heads(remoteheads);
+       strbuf_addch(&merge_msg, '\n');
+       write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
+}
+
 static int default_edit_option(void)
 {
        static const char name[] = "GIT_MERGE_AUTOEDIT";
@@ -1346,7 +1359,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * If head can reach all the merge then we are up to date.
                 * but first the most common case of merging one remote.
                 */
-               finish_up_to_date(_("Already up-to-date."));
+               finish_up_to_date(_("Already up to date."));
                goto done;
        } else if (fast_forward != FF_NO && !remoteheads->next &&
                        !common->next &&
@@ -1429,7 +1442,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        }
                }
                if (up_to_date) {
-                       finish_up_to_date(_("Already up-to-date. Yeeah!"));
+                       finish_up_to_date(_("Already up to date. Yeeah!"));
                        goto done;
                }
        }
index 94fbaaa5dac65cc062429225e22a3586f9997156..ffdd5f01a190b99407d1019972e3198c42b7194c 100644 (file)
@@ -131,7 +131,6 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        struct stat st;
        struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
 
-       gitmodules_config();
        git_config(git_default_config, NULL);
 
        argc = parse_options(argc, argv, prefix, builtin_mv_options,
index c41ea7c2a62ba868061884e511256c24f4d020d9..598da6c8bc75e9de50f85eac78d3051f27f96a6c 100644 (file)
@@ -253,7 +253,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
                struct commit *commit = (struct commit *)o;
                int from_tag = starts_with(path, "refs/tags/");
 
-               if (taggerdate == ULONG_MAX)
+               if (taggerdate == TIME_MAX)
                        taggerdate = ((struct commit *)o)->date;
                path = name_ref_abbrev(path, can_abbreviate_output);
                name_rev(commit, xstrdup(path), taggerdate, 0, 0,
index 82ad6e0c813375b480129c22035afc4d6674992a..a57b4f058dff3ceec80355560bf6a856f130b8c0 100644 (file)
@@ -2171,7 +2171,10 @@ static void *threaded_find_deltas(void *arg)
 {
        struct thread_params *me = arg;
 
+       progress_lock();
        while (me->remaining) {
+               progress_unlock();
+
                find_deltas(me->list, &me->remaining,
                            me->window, me->depth, me->processed);
 
@@ -2193,7 +2196,10 @@ static void *threaded_find_deltas(void *arg)
                        pthread_cond_wait(&me->cond, &me->mutex);
                me->data_ready = 0;
                pthread_mutex_unlock(&me->mutex);
+
+               progress_lock();
        }
+       progress_unlock();
        /* leave ->working 1 so that this doesn't get more work assigned */
        return NULL;
 }
index 97bfde24ba6f85ea9077357f162151d7a4c80e09..419238171d7a2159f5e533bab84408aacb4d35e4 100644 (file)
@@ -38,8 +38,7 @@ static int prune_object(const struct object_id *oid, const char *path,
 void prune_packed_objects(int opts)
 {
        if (opts & PRUNE_PACKED_VERBOSE)
-               progress = start_progress_delay(_("Removing duplicate objects"),
-                       256, 95, 2);
+               progress = start_delayed_progress(_("Removing duplicate objects"), 256);
 
        for_each_loose_file_in_objdir(get_object_directory(),
                                      prune_object, NULL, prune_subdir, &opts);
index c378690545b27b7e4753e0f919f3ea6626b0eae9..cddabf26a95cc22dfcad9843554dc8af26c858bc 100644 (file)
@@ -138,7 +138,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        if (show_progress == -1)
                show_progress = isatty(2);
        if (show_progress)
-               progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2);
+               progress = start_delayed_progress(_("Checking connectivity"), 0);
 
        mark_reachable_objects(&revs, 1, expire, progress);
        stop_progress(&progress);
index d5f618d086365520fcf36ed8db110ba701ba37d3..bf87a2710b31fa49b1802ed297c8ae52eede6354 100644 (file)
@@ -164,8 +164,6 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        argc = parse_options(argc, argv, unused_prefix, read_tree_options,
                             read_tree_usage, 0);
 
-       load_submodule_cache();
-
        hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
        prefix_set = opts.prefix ? 1 : 0;
index f4a85a165bbff187f331a0a4675066fc5f7b4316..3e71a771523d8566590bfbd5de71b08acf79e3a4 100644 (file)
@@ -269,7 +269,7 @@ static void import_object(struct object_id *oid, enum object_type type,
 
                if (fstat(fd, &st) < 0)
                        die_errno("unable to fstat %s", filename);
-               if (index_fd(oid->hash, fd, &st, type, NULL, flags) < 0)
+               if (index_fd(oid, fd, &st, type, NULL, flags) < 0)
                        die("unable to write object to database");
                /* index_fd close()s fd for us */
        }
index 046403ed6881f452271c636e935ad3ab05da0b78..f1af9345e4c72fc6af411037ede717399ea3a4e1 100644 (file)
@@ -75,13 +75,13 @@ static int reset_index(const struct object_id *oid, int reset_type, int quiet)
                struct object_id head_oid;
                if (get_oid("HEAD", &head_oid))
                        return error(_("You do not have a valid HEAD."));
-               if (!fill_tree_descriptor(desc, head_oid.hash))
+               if (!fill_tree_descriptor(desc, &head_oid))
                        return error(_("Failed to find tree of HEAD."));
                nr++;
                opts.fn = twoway_merge;
        }
 
-       if (!fill_tree_descriptor(desc + nr - 1, oid->hash))
+       if (!fill_tree_descriptor(desc + nr - 1, oid))
                return error(_("Failed to find tree of %s."), oid_to_hex(oid));
        if (unpack_trees(nr, desc, &opts))
                return -1;
@@ -156,6 +156,7 @@ static int read_from_tree(const struct pathspec *pathspec,
        opt.output_format = DIFF_FORMAT_CALLBACK;
        opt.format_callback = update_index_from_diff;
        opt.format_callback_data = &intent_to_add;
+       opt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
 
        if (do_diff_cache(tree_oid, &opt))
                return 1;
@@ -308,8 +309,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                                PARSE_OPT_KEEP_DASHDASH);
        parse_args(&pathspec, argv, prefix, patch_mode, &rev);
 
-       load_submodule_cache();
-
        unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
        if (unborn) {
                /* reset on unborn branch: treat as reset to empty tree */
@@ -368,8 +367,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                die_if_unmerged_cache(reset_type);
 
        if (reset_type != SOFT) {
-               struct lock_file *lock = xcalloc(1, sizeof(*lock));
-               hold_locked_index(lock, LOCK_DIE_ON_ERROR);
+               struct lock_file lock = LOCK_INIT;
+               hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
                if (reset_type == MIXED) {
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
                        if (read_from_tree(&pathspec, &oid, intent_to_add))
@@ -385,7 +384,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                die(_("Could not reset index file to revision '%s'."), rev);
                }
 
-               if (write_locked_index(&the_index, lock, COMMIT_LOCK))
+               if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
                        die(_("Could not write new index file."));
        }
 
index 95b4128250c850eb130fbdfea2407bad819a85b8..c1c74d4a7956430fca46fd743946280daf2f0f3f 100644 (file)
@@ -367,7 +367,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                revs.limited = 1;
 
        if (show_progress)
-               progress = start_progress_delay(show_progress, 0, 0, 2);
+               progress = start_delayed_progress(show_progress, 0);
 
        if (use_bitmap_index && !revs.prune) {
                if (revs.count && !revs.left_right && !revs.cherry_mark) {
index 4057e73fa04bee9fce10911955c8a43d118ef573..d91451fea122cec3923543ef78acf0c5ef4c5fa2 100644 (file)
@@ -255,7 +255,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        struct pathspec pathspec;
        char *seen;
 
-       gitmodules_config();
        git_config(git_default_config, NULL);
 
        argc = parse_options(argc, argv, prefix, builtin_rm_options,
index 0ff9dd0b85074dfe6e04b6fc56c04f2c1789f7eb..818fe74f0af8696fe21a75d4aaf6b919b645d22b 100644 (file)
@@ -275,8 +275,6 @@ static void module_list_active(struct module_list *list)
        int i;
        struct module_list active_modules = MODULE_LIST_INIT;
 
-       gitmodules_config();
-
        for (i = 0; i < list->nr; i++) {
                const struct cache_entry *ce = list->entries[i];
 
@@ -337,9 +335,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
        struct strbuf sb = STRBUF_INIT;
        char *upd = NULL, *url = NULL, *displaypath;
 
-       /* Only loads from .gitmodules, no overlay with .git/config */
-       gitmodules_config();
-
        if (prefix && get_super_prefix())
                die("BUG: cannot have prefix and superprefix");
        else if (prefix)
@@ -475,7 +470,6 @@ static int module_name(int argc, const char **argv, const char *prefix)
        if (argc != 2)
                usage(_("git submodule--helper name <path>"));
 
-       gitmodules_config();
        sub = submodule_from_path(&null_oid, argv[1]);
 
        if (!sub)
@@ -780,6 +774,10 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                                           struct strbuf *out)
 {
        const struct submodule *sub = NULL;
+       const char *url = NULL;
+       const char *update_string;
+       enum submodule_update_type update_type;
+       char *key;
        struct strbuf displaypath_sb = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
        const char *displaypath = NULL;
@@ -808,9 +806,17 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                goto cleanup;
        }
 
+       key = xstrfmt("submodule.%s.update", sub->name);
+       if (!repo_config_get_string_const(the_repository, key, &update_string)) {
+               update_type = parse_submodule_update_type(update_string);
+       } else {
+               update_type = sub->update_strategy.type;
+       }
+       free(key);
+
        if (suc->update.type == SM_UPDATE_NONE
            || (suc->update.type == SM_UPDATE_UNSPECIFIED
-               && sub->update_strategy.type == SM_UPDATE_NONE)) {
+               && update_type == SM_UPDATE_NONE)) {
                strbuf_addf(out, _("Skipping submodule '%s'"), displaypath);
                strbuf_addch(out, '\n');
                goto cleanup;
@@ -822,6 +828,11 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                goto cleanup;
        }
 
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "submodule.%s.url", sub->name);
+       if (repo_config_get_string_const(the_repository, sb.buf, &url))
+               url = sub->url;
+
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/.git", ce->name);
        needs_cloning = !file_exists(sb.buf);
@@ -851,7 +862,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                argv_array_push(&child->args, "--depth=1");
        argv_array_pushl(&child->args, "--path", sub->path, NULL);
        argv_array_pushl(&child->args, "--name", sub->name, NULL);
-       argv_array_pushl(&child->args, "--url", sub->url, NULL);
+       argv_array_pushl(&child->args, "--url", url, NULL);
        if (suc->references.nr) {
                struct string_list_item *item;
                for_each_string_list_item(item, &suc->references)
@@ -1025,10 +1036,6 @@ static int update_clone(int argc, const char **argv, const char *prefix)
        if (pathspec.nr)
                suc.warn_if_uninitialized = 1;
 
-       /* Overlay the parsed .gitmodules file with .git/config */
-       gitmodules_config();
-       git_config(submodule_config, NULL);
-
        run_processes_parallel(max_jobs,
                               update_clone_get_next_task,
                               update_clone_start_failure,
@@ -1066,17 +1073,22 @@ static int resolve_relative_path(int argc, const char **argv, const char *prefix
 static const char *remote_submodule_branch(const char *path)
 {
        const struct submodule *sub;
-       gitmodules_config();
-       git_config(submodule_config, NULL);
+       const char *branch = NULL;
+       char *key;
 
        sub = submodule_from_path(&null_oid, path);
        if (!sub)
                return NULL;
 
-       if (!sub->branch)
+       key = xstrfmt("submodule.%s.branch", sub->name);
+       if (repo_config_get_string_const(the_repository, key, &branch))
+               branch = sub->branch;
+       free(key);
+
+       if (!branch)
                return "master";
 
-       if (!strcmp(sub->branch, ".")) {
+       if (!strcmp(branch, ".")) {
                unsigned char sha1[20];
                const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
 
@@ -1094,7 +1106,7 @@ static const char *remote_submodule_branch(const char *path)
                return refname;
        }
 
-       return sub->branch;
+       return branch;
 }
 
 static int resolve_remote_submodule_branch(int argc, const char **argv,
@@ -1213,9 +1225,6 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, embed_gitdir_options,
                             git_submodule_helper_usage, 0);
 
-       gitmodules_config();
-       git_config(submodule_config, NULL);
-
        if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
                return 1;
 
@@ -1231,8 +1240,6 @@ static int is_active(int argc, const char **argv, const char *prefix)
        if (argc != 2)
                die("submodule--helper is-active takes exactly 1 argument");
 
-       gitmodules_config();
-
        return !is_submodule_active(the_repository, argv[1]);
 }
 
index 56721cf03db23a2f5a1b8e9419422df916d7b00f..655e6d60d3f7e0525d5b88c66803e7b993168443 100644 (file)
@@ -280,7 +280,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
        fill_stat_cache_info(ce, st);
        ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
-       if (index_path(ce->oid.hash, path, st,
+       if (index_path(&ce->oid, path, st,
                       info_only ? 0 : HASH_WRITE_OBJECT)) {
                free(ce);
                return -1;
@@ -915,7 +915,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        struct refresh_params refresh_args = {0, &has_errors};
        int lock_error = 0;
        int split_index = -1;
-       struct lock_file *lock_file;
+       struct lock_file lock_file = LOCK_INIT;
        struct parse_opt_ctx_t ctx;
        strbuf_getline_fn getline_fn;
        int parseopt_state = PARSE_OPT_UNKNOWN;
@@ -1014,11 +1014,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
 
-       /* We can't free this memory, it becomes part of a linked list parsed atexit() */
-       lock_file = xcalloc(1, sizeof(struct lock_file));
-
        /* we will diagnose later if it turns out that we need to update it */
-       newfd = hold_locked_index(lock_file, 0);
+       newfd = hold_locked_index(&lock_file, 0);
        if (newfd < 0)
                lock_error = errno;
 
@@ -1153,11 +1150,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                                exit(128);
                        unable_to_lock_die(get_index_file(), lock_error);
                }
-               if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die("Unable to write new index file");
        }
 
-       rollback_lock_file(lock_file);
+       rollback_lock_file(&lock_file);
 
        return has_errors ? 1 : 0;
 }
index 2440d1dc89175efaf74286da4b2c8f97fc52fbd9..71d092ed514549f03407aef4fd17ab1467191c68 100644 (file)
@@ -603,19 +603,16 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat
 int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, const char *index_path, int flags, const char *prefix)
 {
        int entries, was_valid, newfd;
-       struct lock_file *lock_file;
+       struct lock_file lock_file = LOCK_INIT;
+       int ret = 0;
 
-       /*
-        * We can't free this memory, it becomes part of a linked list
-        * parsed atexit()
-        */
-       lock_file = xcalloc(1, sizeof(struct lock_file));
-
-       newfd = hold_lock_file_for_update(lock_file, index_path, LOCK_DIE_ON_ERROR);
+       newfd = hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
 
        entries = read_index_from(index_state, index_path);
-       if (entries < 0)
-               return WRITE_TREE_UNREADABLE_INDEX;
+       if (entries < 0) {
+               ret = WRITE_TREE_UNREADABLE_INDEX;
+               goto out;
+       }
        if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
                cache_tree_free(&index_state->cache_tree);
 
@@ -624,10 +621,12 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co
 
        was_valid = cache_tree_fully_valid(index_state->cache_tree);
        if (!was_valid) {
-               if (cache_tree_update(index_state, flags) < 0)
-                       return WRITE_TREE_UNMERGED_INDEX;
+               if (cache_tree_update(index_state, flags) < 0) {
+                       ret = WRITE_TREE_UNMERGED_INDEX;
+                       goto out;
+               }
                if (0 <= newfd) {
-                       if (!write_locked_index(index_state, lock_file, COMMIT_LOCK))
+                       if (!write_locked_index(index_state, &lock_file, COMMIT_LOCK))
                                newfd = -1;
                }
                /* Not being able to write is fine -- we are only interested
@@ -641,17 +640,19 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co
        if (prefix) {
                struct cache_tree *subtree;
                subtree = cache_tree_find(index_state->cache_tree, prefix);
-               if (!subtree)
-                       return WRITE_TREE_PREFIX_ERROR;
+               if (!subtree) {
+                       ret = WRITE_TREE_PREFIX_ERROR;
+                       goto out;
+               }
                hashcpy(sha1, subtree->oid.hash);
        }
        else
                hashcpy(sha1, index_state->cache_tree->oid.hash);
 
+out:
        if (0 <= newfd)
-               rollback_lock_file(lock_file);
-
-       return 0;
+               rollback_lock_file(&lock_file);
+       return ret;
 }
 
 int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
diff --git a/cache.h b/cache.h
index 2f09f8814aadc15dbdec58e06da1e109742ffcd6..a916bc79e3c1a77f55764f34f6357478e83b6cb0 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -684,8 +684,8 @@ extern int ie_modified(const struct index_state *, const struct cache_entry *, s
 
 #define HASH_WRITE_OBJECT 1
 #define HASH_FORMAT_CHECK 2
-extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
-extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
+extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
+extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
 
 /*
  * Record to sd the data from st that we use to check whether a file
@@ -1178,7 +1178,7 @@ static inline const unsigned char *lookup_replace_object(const unsigned char *sh
 extern int sha1_object_info(const unsigned char *, unsigned long *);
 extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
 extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
-extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
+extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, struct object_id *oid, unsigned flags);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 extern int git_open_cloexec(const char *name, int flags);
@@ -1835,6 +1835,8 @@ void shift_tree_by(const struct object_id *, const struct object_id *, struct ob
 #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
 #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
 #define WS_TAB_WIDTH_MASK        077
+/* All WS_* -- when extended, adapt diff.c emit_symbol */
+#define WS_RULE_MASK           07777
 extern unsigned whitespace_rule_cfg;
 extern unsigned whitespace_rule(const char *);
 extern unsigned parse_whitespace_rule(const char *);
diff --git a/color.c b/color.c
index 7aa8b076f045e5c0b74826153030e2923c0fc56e..9ccd954d6ba5cea1dd275cac23bb19966b9c6386 100644 (file)
--- a/color.c
+++ b/color.c
@@ -338,6 +338,13 @@ static int check_auto_color(void)
 
 int want_color(int var)
 {
+       /*
+        * NEEDSWORK: This function is sometimes used from multiple threads, and
+        * we end up using want_auto racily. That "should not matter" since
+        * we always write the same value, but it's still wrong. This function
+        * is listed in .tsan-suppressions for the time being.
+        */
+
        static int want_auto = -1;
 
        if (var < 0)
diff --git a/color.h b/color.h
index 90627650fccc67c31261977a22544af131812bd1..fd2b688dfbccbe4eef4c1f79b680db2a01e2f9ef 100644 (file)
--- a/color.h
+++ b/color.h
@@ -42,6 +42,8 @@ struct strbuf;
 #define GIT_COLOR_BG_BLUE      "\033[44m"
 #define GIT_COLOR_BG_MAGENTA   "\033[45m"
 #define GIT_COLOR_BG_CYAN      "\033[46m"
+#define GIT_COLOR_FAINT                "\033[2m"
+#define GIT_COLOR_FAINT_ITALIC "\033[2;3m"
 
 /* A special value meaning "no color selected" */
 #define GIT_COLOR_NIL "NIL"
index 8b28415939035e2b40153013e6f8805422094d84..17a93d1e64fd7122ce0d3471a85388cd445ab6b2 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -134,35 +134,41 @@ int register_commit_graft(struct commit_graft *graft, int ignore_dups)
        return 0;
 }
 
-struct commit_graft *read_graft_line(char *buf, int len)
+struct commit_graft *read_graft_line(struct strbuf *line)
 {
        /* The format is just "Commit Parent1 Parent2 ...\n" */
-       int i;
+       int i, phase;
+       const char *tail = NULL;
        struct commit_graft *graft = NULL;
-       const int entry_size = GIT_SHA1_HEXSZ + 1;
+       struct object_id dummy_oid, *oid;
 
-       while (len && isspace(buf[len-1]))
-               buf[--len] = '\0';
-       if (buf[0] == '#' || buf[0] == '\0')
+       strbuf_rtrim(line);
+       if (!line->len || line->buf[0] == '#')
                return NULL;
-       if ((len + 1) % entry_size)
-               goto bad_graft_data;
-       i = (len + 1) / entry_size - 1;
-       graft = xmalloc(st_add(sizeof(*graft), st_mult(GIT_SHA1_RAWSZ, i)));
-       graft->nr_parent = i;
-       if (get_oid_hex(buf, &graft->oid))
-               goto bad_graft_data;
-       for (i = GIT_SHA1_HEXSZ; i < len; i += entry_size) {
-               if (buf[i] != ' ')
-                       goto bad_graft_data;
-               if (get_sha1_hex(buf + i + 1, graft->parent[i/entry_size].hash))
+       /*
+        * phase 0 verifies line, counts hashes in line and allocates graft
+        * phase 1 fills graft
+        */
+       for (phase = 0; phase < 2; phase++) {
+               oid = graft ? &graft->oid : &dummy_oid;
+               if (parse_oid_hex(line->buf, oid, &tail))
                        goto bad_graft_data;
+               for (i = 0; *tail != '\0'; i++) {
+                       oid = graft ? &graft->parent[i] : &dummy_oid;
+                       if (!isspace(*tail++) || parse_oid_hex(tail, oid, &tail))
+                               goto bad_graft_data;
+               }
+               if (!graft) {
+                       graft = xmalloc(st_add(sizeof(*graft),
+                                              st_mult(sizeof(struct object_id), i)));
+                       graft->nr_parent = i;
+               }
        }
        return graft;
 
 bad_graft_data:
-       error("bad graft data: %s", buf);
-       free(graft);
+       error("bad graft data: %s", line->buf);
+       assert(!graft);
        return NULL;
 }
 
@@ -174,7 +180,7 @@ static int read_graft_file(const char *graft_file)
                return -1;
        while (!strbuf_getwholeline(&buf, fp, '\n')) {
                /* The format is just "Commit Parent1 Parent2 ...\n" */
-               struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
+               struct commit_graft *graft = read_graft_line(&buf);
                if (!graft)
                        continue;
                if (register_commit_graft(graft, 1))
index 6d857f06c16c0dfc3a447355d9a185b416e4e664..6d769590f285e534d20bfd8108e8e980253f8815 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -247,7 +247,7 @@ struct commit_graft {
 };
 typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
 
-struct commit_graft *read_graft_line(char *buf, int len);
+struct commit_graft *read_graft_line(struct strbuf *line);
 int register_commit_graft(struct commit_graft *, int);
 struct commit_graft *lookup_commit_graft(const struct object_id *oid);
 
@@ -313,11 +313,6 @@ extern int interactive_add(int argc, const char **argv, const char *prefix, int
 extern int run_add_interactive(const char *revision, const char *patch_mode,
                               const struct pathspec *pathspec);
 
-static inline int single_parent(struct commit *commit)
-{
-       return commit->parents && !commit->parents->next;
-}
-
 struct commit_list *reduce_heads(struct commit_list *heads);
 
 struct commit_extra_header {
index 777527daef8a292ec6a3589319c5d84593b3d487..7931182a547b2e1a627c6dc95061e357abe2febd 100644 (file)
--- a/config.c
+++ b/config.c
@@ -2094,6 +2094,28 @@ int git_config_get_expiry(const char *key, const char **output)
        return ret;
 }
 
+int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestamp_t now)
+{
+       char *expiry_string;
+       intmax_t days;
+       timestamp_t when;
+
+       if (git_config_get_string(key, &expiry_string))
+               return 1; /* no such thing */
+
+       if (git_parse_signed(expiry_string, &days, maximum_signed_value_of_type(int))) {
+               const int scale = 86400;
+               *expiry = now - days * scale;
+               return 0;
+       }
+
+       if (!parse_expiry_date(expiry_string, &when)) {
+               *expiry = when;
+               return 0;
+       }
+       return -1; /* thing exists but cannot be parsed */
+}
+
 int git_config_get_untracked_cache(void)
 {
        int val = -1;
@@ -2428,7 +2450,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
 {
        int fd = -1, in_fd = -1;
        int ret;
-       struct lock_file *lock = NULL;
+       struct lock_file lock = LOCK_INIT;
        char *filename_buf = NULL;
        char *contents = NULL;
        size_t contents_sz;
@@ -2447,8 +2469,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
         * The lock serves a purpose in addition to locking: the new
         * contents of .git/config will be written into it.
         */
-       lock = xcalloc(1, sizeof(struct lock_file));
-       fd = hold_lock_file_for_update(lock, config_filename, 0);
+       fd = hold_lock_file_for_update(&lock, config_filename, 0);
        if (fd < 0) {
                error_errno("could not lock config file %s", config_filename);
                free(store.key);
@@ -2561,8 +2582,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                close(in_fd);
                in_fd = -1;
 
-               if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
-                       error_errno("chmod on %s failed", get_lock_file_path(lock));
+               if (chmod(get_lock_file_path(&lock), st.st_mode & 07777) < 0) {
+                       error_errno("chmod on %s failed", get_lock_file_path(&lock));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
@@ -2617,28 +2638,19 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                contents = NULL;
        }
 
-       if (commit_lock_file(lock) < 0) {
+       if (commit_lock_file(&lock) < 0) {
                error_errno("could not write config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
-               lock = NULL;
                goto out_free;
        }
 
-       /*
-        * lock is committed, so don't try to roll it back below.
-        * NOTE: Since lockfile.c keeps a linked list of all created
-        * lock_file structures, it isn't safe to free(lock).  It's
-        * better to just leave it hanging around.
-        */
-       lock = NULL;
        ret = 0;
 
        /* Invalidate the config cache */
        git_config_clear();
 
 out_free:
-       if (lock)
-               rollback_lock_file(lock);
+       rollback_lock_file(&lock);
        free(filename_buf);
        if (contents)
                munmap(contents, contents_sz);
@@ -2647,7 +2659,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        return ret;
 
 write_err_out:
-       ret = write_error(get_lock_file_path(lock));
+       ret = write_error(get_lock_file_path(&lock));
        goto out_free;
 
 }
index 18b6f3f724bd823bf98f039e4699b0c2c5dc5393..97471b887320b48f1b4c2d5a2023f6ffd1acf94e 100644 (file)
--- a/config.h
+++ b/config.h
@@ -215,6 +215,9 @@ extern int git_config_get_max_percent_split_change(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);
 
+/* 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);
+
 struct key_value_info {
        const char *filename;
        int linenr;
index fbf5c582496eece56b29a91a2f3085716f4b0e9f..f2be7cc92437191219d16ddf1536883da23541c0 100644 (file)
@@ -17,4 +17,7 @@ shebang.perl: FORCE
 test: all
        $(MAKE) -C t
 
+clean:
+       $(RM) diff-highlight
+
 .PHONY: FORCE
index ee99f1a4ee89a9e9f358b6cc89fc84cab554b439..932e78dbfee97750ae38afcf699e95f118f956dd 100755 (executable)
@@ -399,7 +399,7 @@ case "$allow_fast_forward,$#,$common,$no_commit" in
 ?,1,"$1",*)
        # If head can reach all the merge then we are up to date.
        # but first the most common case of merging one remote.
-       finish_up_to_date "Already up-to-date."
+       finish_up_to_date "Already up to date."
        exit 0
        ;;
 t,1,"$head",*)
@@ -459,7 +459,7 @@ t,1,"$head",*)
        done
        if test "$up_to_date" = t
        then
-               finish_up_to_date "Already up-to-date. Yeeah!"
+               finish_up_to_date "Already up to date. Yeeah!"
                exit 0
        fi
        ;;
index 70fdc27b724f28793c6a4bff713957a22689b78b..3099dc851ac26a9280e7e6d1a73638c05d01cc17 100755 (executable)
@@ -41,7 +41,7 @@ fi
 
 case "$common" in
 "$merge")
-       echo "Already up-to-date. Yeeah!"
+       echo "Already up to date. Yeeah!"
        dropheads
        exit 0
        ;;
index 3c87ebaf57baede9810c3b9b489eed9751189706..d05c613c9718697f64bc2334dc9ae85bf473f13e 100755 (executable)
@@ -253,7 +253,7 @@ test_expect_success 'merge the added subproj again, should do nothing' '
                # this shouldn not actually do anything, since FETCH_HEAD
                # is already a parent
                result=$(git merge -s ours -m "merge -s -ours" FETCH_HEAD) &&
-               check_equal "${result}" "Already up-to-date."
+               check_equal "${result}" "Already up to date."
        )
 '
 
index 1012462e3c9c114ad1b2374a2ae27568ace57bac..02962261c58f71485a27f9799a5db29ec77e0bc7 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1040,7 +1040,6 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)
                ca->crlf_action = git_path_check_crlf(ccheck + 4);
                if (ca->crlf_action == CRLF_UNDEFINED)
                        ca->crlf_action = git_path_check_crlf(ccheck + 0);
-               ca->attr_action = ca->crlf_action;
                ca->ident = git_path_check_ident(ccheck + 1);
                ca->drv = git_path_check_convert(ccheck + 2);
                if (ca->crlf_action != CRLF_BINARY) {
@@ -1054,12 +1053,14 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)
                        else if (eol_attr == EOL_CRLF)
                                ca->crlf_action = CRLF_TEXT_CRLF;
                }
-               ca->attr_action = ca->crlf_action;
        } else {
                ca->drv = NULL;
                ca->crlf_action = CRLF_UNDEFINED;
                ca->ident = 0;
        }
+
+       /* Save attr and make a decision for action */
+       ca->attr_action = ca->crlf_action;
        if (ca->crlf_action == CRLF_TEXT)
                ca->crlf_action = text_eol_is_crlf() ? CRLF_TEXT_CRLF : CRLF_TEXT_INPUT;
        if (ca->crlf_action == CRLF_UNDEFINED && auto_crlf == AUTO_CRLF_FALSE)
@@ -1132,10 +1133,12 @@ int convert_to_git(const struct index_state *istate,
                src = dst->buf;
                len = dst->len;
        }
-       ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
-       if (ret && dst) {
-               src = dst->buf;
-               len = dst->len;
+       if (checksafe != SAFE_CRLF_KEEP_CRLF) {
+               ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
+               if (ret && dst) {
+                       src = dst->buf;
+                       len = dst->len;
+               }
        }
        return ret | ident_to_git(path, src, len, dst, ca.ident);
 }
index 6b06144281c797a08c10e0dc5bf72f97d260be88..4f2da225a8926f92e465c7dea27cdc4589864e1f 100644 (file)
--- a/convert.h
+++ b/convert.h
@@ -12,7 +12,8 @@ enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
        SAFE_CRLF_FAIL = 1,
        SAFE_CRLF_WARN = 2,
-       SAFE_CRLF_RENORMALIZE = 3
+       SAFE_CRLF_RENORMALIZE = 3,
+       SAFE_CRLF_KEEP_CRLF = 4
 };
 
 extern enum safe_crlf safe_crlf;
index 0d5c6250940633e75e2c9fb4b59b242a52188411..4dfbc8c9f917a600c0995b7e33927df3fa19453d 100644 (file)
@@ -5,8 +5,6 @@
 #include "unix-socket.h"
 #include "parse-options.h"
 
-static struct tempfile socket_file;
-
 struct credential_cache_entry {
        struct credential item;
        timestamp_t expiration;
@@ -260,6 +258,7 @@ static void init_socket_directory(const char *path)
 
 int cmd_main(int argc, const char **argv)
 {
+       struct tempfile *socket_file;
        const char *socket_path;
        int ignore_sighup = 0;
        static const char *usage[] = {
@@ -285,7 +284,7 @@ int cmd_main(int argc, const char **argv)
                die("socket directory must be an absolute path");
 
        init_socket_directory(socket_path);
-       register_tempfile(&socket_file, socket_path);
+       socket_file = register_tempfile(socket_path);
 
        if (ignore_sighup)
                signal(SIGHUP, SIG_IGN);
diff --git a/diff.c b/diff.c
index 4c60990f7058fda57d0ed450df51aa251065b3ab..7df2227a3c9af562305fe91e532d9c56759809d0 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -16,6 +16,7 @@
 #include "userdiff.h"
 #include "submodule-config.h"
 #include "submodule.h"
+#include "hashmap.h"
 #include "ll-merge.h"
 #include "string-list.h"
 #include "argv-array.h"
@@ -33,6 +34,7 @@ static int diff_indent_heuristic = 1;
 static int diff_rename_limit_default = 400;
 static int diff_suppress_blank_empty;
 static int diff_use_color_default = -1;
+static int diff_color_moved_default;
 static int diff_context_default = 3;
 static int diff_interhunk_context_default;
 static const char *diff_word_regex_cfg;
@@ -57,6 +59,14 @@ static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_YELLOW,       /* COMMIT */
        GIT_COLOR_BG_RED,       /* WHITESPACE */
        GIT_COLOR_NORMAL,       /* FUNCINFO */
+       GIT_COLOR_BOLD_MAGENTA, /* OLD_MOVED */
+       GIT_COLOR_BOLD_BLUE,    /* OLD_MOVED ALTERNATIVE */
+       GIT_COLOR_FAINT,        /* OLD_MOVED_DIM */
+       GIT_COLOR_FAINT_ITALIC, /* OLD_MOVED_ALTERNATIVE_DIM */
+       GIT_COLOR_BOLD_CYAN,    /* NEW_MOVED */
+       GIT_COLOR_BOLD_YELLOW,  /* NEW_MOVED ALTERNATIVE */
+       GIT_COLOR_FAINT,        /* NEW_MOVED_DIM */
+       GIT_COLOR_FAINT_ITALIC, /* NEW_MOVED_ALTERNATIVE_DIM */
 };
 
 static NORETURN void die_want_option(const char *option_name)
@@ -82,6 +92,22 @@ static int parse_diff_color_slot(const char *var)
                return DIFF_WHITESPACE;
        if (!strcasecmp(var, "func"))
                return DIFF_FUNCINFO;
+       if (!strcasecmp(var, "oldmoved"))
+               return DIFF_FILE_OLD_MOVED;
+       if (!strcasecmp(var, "oldmovedalternative"))
+               return DIFF_FILE_OLD_MOVED_ALT;
+       if (!strcasecmp(var, "oldmoveddimmed"))
+               return DIFF_FILE_OLD_MOVED_DIM;
+       if (!strcasecmp(var, "oldmovedalternativedimmed"))
+               return DIFF_FILE_OLD_MOVED_ALT_DIM;
+       if (!strcasecmp(var, "newmoved"))
+               return DIFF_FILE_NEW_MOVED;
+       if (!strcasecmp(var, "newmovedalternative"))
+               return DIFF_FILE_NEW_MOVED_ALT;
+       if (!strcasecmp(var, "newmoveddimmed"))
+               return DIFF_FILE_NEW_MOVED_DIM;
+       if (!strcasecmp(var, "newmovedalternativedimmed"))
+               return DIFF_FILE_NEW_MOVED_ALT_DIM;
        return -1;
 }
 
@@ -230,12 +256,44 @@ int git_diff_heuristic_config(const char *var, const char *value, void *cb)
        return 0;
 }
 
+static int parse_color_moved(const char *arg)
+{
+       switch (git_parse_maybe_bool(arg)) {
+       case 0:
+               return COLOR_MOVED_NO;
+       case 1:
+               return COLOR_MOVED_DEFAULT;
+       default:
+               break;
+       }
+
+       if (!strcmp(arg, "no"))
+               return COLOR_MOVED_NO;
+       else if (!strcmp(arg, "plain"))
+               return COLOR_MOVED_PLAIN;
+       else if (!strcmp(arg, "zebra"))
+               return COLOR_MOVED_ZEBRA;
+       else if (!strcmp(arg, "default"))
+               return COLOR_MOVED_DEFAULT;
+       else if (!strcmp(arg, "dimmed_zebra"))
+               return COLOR_MOVED_ZEBRA_DIM;
+       else
+               return error(_("color moved setting must be one of 'no', 'default', 'zebra', 'dimmed_zebra', 'plain'"));
+}
+
 int git_diff_ui_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
                diff_use_color_default = git_config_colorbool(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.colormoved")) {
+               int cm = parse_color_moved(value);
+               if (cm < 0)
+                       return -1;
+               diff_color_moved_default = cm;
+               return 0;
+       }
        if (!strcmp(var, "diff.context")) {
                diff_context_default = git_config_int(var, value);
                if (diff_context_default < 0)
@@ -344,9 +402,6 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (starts_with(var, "submodule."))
-               return parse_submodule_config_option(var, value);
-
        if (git_diff_heuristic_config(var, value, cb) < 0)
                return -1;
 
@@ -404,7 +459,7 @@ static struct diff_tempfile {
         * If this diff_tempfile instance refers to a temporary file,
         * this tempfile object is used to manage its lifetime.
         */
-       struct tempfile tempfile;
+       struct tempfile *tempfile;
 } diff_temp[2];
 
 struct emit_callback {
@@ -555,68 +610,735 @@ static void emit_line(struct diff_options *o, const char *set, const char *reset
        emit_line_0(o, set, reset, line[0], line+1, len-1);
 }
 
-static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
+enum diff_symbol {
+       DIFF_SYMBOL_BINARY_DIFF_HEADER,
+       DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA,
+       DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL,
+       DIFF_SYMBOL_BINARY_DIFF_BODY,
+       DIFF_SYMBOL_BINARY_DIFF_FOOTER,
+       DIFF_SYMBOL_STATS_SUMMARY_NO_FILES,
+       DIFF_SYMBOL_STATS_SUMMARY_ABBREV,
+       DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES,
+       DIFF_SYMBOL_STATS_LINE,
+       DIFF_SYMBOL_WORD_DIFF,
+       DIFF_SYMBOL_STAT_SEP,
+       DIFF_SYMBOL_SUMMARY,
+       DIFF_SYMBOL_SUBMODULE_ADD,
+       DIFF_SYMBOL_SUBMODULE_DEL,
+       DIFF_SYMBOL_SUBMODULE_UNTRACKED,
+       DIFF_SYMBOL_SUBMODULE_MODIFIED,
+       DIFF_SYMBOL_SUBMODULE_HEADER,
+       DIFF_SYMBOL_SUBMODULE_ERROR,
+       DIFF_SYMBOL_SUBMODULE_PIPETHROUGH,
+       DIFF_SYMBOL_REWRITE_DIFF,
+       DIFF_SYMBOL_BINARY_FILES,
+       DIFF_SYMBOL_HEADER,
+       DIFF_SYMBOL_FILEPAIR_PLUS,
+       DIFF_SYMBOL_FILEPAIR_MINUS,
+       DIFF_SYMBOL_WORDS_PORCELAIN,
+       DIFF_SYMBOL_WORDS,
+       DIFF_SYMBOL_CONTEXT,
+       DIFF_SYMBOL_CONTEXT_INCOMPLETE,
+       DIFF_SYMBOL_PLUS,
+       DIFF_SYMBOL_MINUS,
+       DIFF_SYMBOL_NO_LF_EOF,
+       DIFF_SYMBOL_CONTEXT_FRAGINFO,
+       DIFF_SYMBOL_CONTEXT_MARKER,
+       DIFF_SYMBOL_SEPARATOR
+};
+/*
+ * Flags for content lines:
+ * 0..12 are whitespace rules
+ * 13-15 are WSEH_NEW | WSEH_OLD | WSEH_CONTEXT
+ * 16 is marking if the line is blank at EOF
+ */
+#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF     (1<<16)
+#define DIFF_SYMBOL_MOVED_LINE                 (1<<17)
+#define DIFF_SYMBOL_MOVED_LINE_ALT             (1<<18)
+#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING   (1<<19)
+#define DIFF_SYMBOL_CONTENT_WS_MASK (WSEH_NEW | WSEH_OLD | WSEH_CONTEXT | WS_RULE_MASK)
+
+/*
+ * This struct is used when we need to buffer the output of the diff output.
+ *
+ * NEEDSWORK: Instead of storing a copy of the line, add an offset pointer
+ * into the pre/post image file. This pointer could be a union with the
+ * line pointer. By storing an offset into the file instead of the literal line,
+ * we can decrease the memory footprint for the buffered output. At first we
+ * may want to only have indirection for the content lines, but we could also
+ * enhance the state for emitting prefabricated lines, e.g. the similarity
+ * score line or hunk/file headers would only need to store a number or path
+ * and then the output can be constructed later on depending on state.
+ */
+struct emitted_diff_symbol {
+       const char *line;
+       int len;
+       int flags;
+       enum diff_symbol s;
+};
+#define EMITTED_DIFF_SYMBOL_INIT {NULL}
+
+struct emitted_diff_symbols {
+       struct emitted_diff_symbol *buf;
+       int nr, alloc;
+};
+#define EMITTED_DIFF_SYMBOLS_INIT {NULL, 0, 0}
+
+static void append_emitted_diff_symbol(struct diff_options *o,
+                                      struct emitted_diff_symbol *e)
 {
-       if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
-             ecbdata->blank_at_eof_in_preimage &&
-             ecbdata->blank_at_eof_in_postimage &&
-             ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
-             ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
-               return 0;
-       return ws_blank_line(line, len, ecbdata->ws_rule);
+       struct emitted_diff_symbol *f;
+
+       ALLOC_GROW(o->emitted_symbols->buf,
+                  o->emitted_symbols->nr + 1,
+                  o->emitted_symbols->alloc);
+       f = &o->emitted_symbols->buf[o->emitted_symbols->nr++];
+
+       memcpy(f, e, sizeof(struct emitted_diff_symbol));
+       f->line = e->line ? xmemdupz(e->line, e->len) : NULL;
 }
 
-static void emit_line_checked(const char *reset,
-                             struct emit_callback *ecbdata,
-                             const char *line, int len,
-                             enum color_diff color,
-                             unsigned ws_error_highlight,
-                             char sign)
+struct moved_entry {
+       struct hashmap_entry ent;
+       const struct emitted_diff_symbol *es;
+       struct moved_entry *next_line;
+};
+
+static int next_byte(const char **cp, const char **endp,
+                    const struct diff_options *diffopt)
+{
+       int retval;
+
+       if (*cp > *endp)
+               return -1;
+
+       if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_CHANGE)) {
+               while (*cp < *endp && isspace(**cp))
+                       (*cp)++;
+               /*
+                * After skipping a couple of whitespaces, we still have to
+                * account for one space.
+                */
+               return (int)' ';
+       }
+
+       if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE)) {
+               while (*cp < *endp && isspace(**cp))
+                       (*cp)++;
+               /* return the first non-ws character via the usual below */
+       }
+
+       retval = (unsigned char)(**cp);
+       (*cp)++;
+       return retval;
+}
+
+static int moved_entry_cmp(const struct diff_options *diffopt,
+                          const struct moved_entry *a,
+                          const struct moved_entry *b,
+                          const void *keydata)
+{
+       const char *ap = a->es->line, *ae = a->es->line + a->es->len;
+       const char *bp = b->es->line, *be = b->es->line + b->es->len;
+
+       if (!(diffopt->xdl_opts & XDF_WHITESPACE_FLAGS))
+               return a->es->len != b->es->len  || memcmp(ap, bp, a->es->len);
+
+       if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_AT_EOL)) {
+               while (ae > ap && isspace(*ae))
+                       ae--;
+               while (be > bp && isspace(*be))
+                       be--;
+       }
+
+       while (1) {
+               int ca, cb;
+               ca = next_byte(&ap, &ae, diffopt);
+               cb = next_byte(&bp, &be, diffopt);
+               if (ca != cb)
+                       return 1;
+               if (ca < 0)
+                       return 0;
+       }
+}
+
+static unsigned get_string_hash(struct emitted_diff_symbol *es, struct diff_options *o)
+{
+       if (o->xdl_opts & XDF_WHITESPACE_FLAGS) {
+               static struct strbuf sb = STRBUF_INIT;
+               const char *ap = es->line, *ae = es->line + es->len;
+               int c;
+
+               strbuf_reset(&sb);
+               while (ae > ap && isspace(*ae))
+                       ae--;
+               while ((c = next_byte(&ap, &ae, o)) > 0)
+                       strbuf_addch(&sb, c);
+
+               return memhash(sb.buf, sb.len);
+       } else {
+               return memhash(es->line, es->len);
+       }
+}
+
+static struct moved_entry *prepare_entry(struct diff_options *o,
+                                        int line_no)
+{
+       struct moved_entry *ret = xmalloc(sizeof(*ret));
+       struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
+
+       ret->ent.hash = get_string_hash(l, o);
+       ret->es = l;
+       ret->next_line = NULL;
+
+       return ret;
+}
+
+static void add_lines_to_move_detection(struct diff_options *o,
+                                       struct hashmap *add_lines,
+                                       struct hashmap *del_lines)
+{
+       struct moved_entry *prev_line = NULL;
+
+       int n;
+       for (n = 0; n < o->emitted_symbols->nr; n++) {
+               struct hashmap *hm;
+               struct moved_entry *key;
+
+               switch (o->emitted_symbols->buf[n].s) {
+               case DIFF_SYMBOL_PLUS:
+                       hm = add_lines;
+                       break;
+               case DIFF_SYMBOL_MINUS:
+                       hm = del_lines;
+                       break;
+               default:
+                       prev_line = NULL;
+                       continue;
+               }
+
+               key = prepare_entry(o, n);
+               if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
+                       prev_line->next_line = key;
+
+               hashmap_add(hm, key);
+               prev_line = key;
+       }
+}
+
+static int shrink_potential_moved_blocks(struct moved_entry **pmb,
+                                        int pmb_nr)
+{
+       int lp, rp;
+
+       /* Shrink the set of potential block to the remaining running */
+       for (lp = 0, rp = pmb_nr - 1; lp <= rp;) {
+               while (lp < pmb_nr && pmb[lp])
+                       lp++;
+               /* lp points at the first NULL now */
+
+               while (rp > -1 && !pmb[rp])
+                       rp--;
+               /* rp points at the last non-NULL */
+
+               if (lp < pmb_nr && rp > -1 && lp < rp) {
+                       pmb[lp] = pmb[rp];
+                       pmb[rp] = NULL;
+                       rp--;
+                       lp++;
+               }
+       }
+
+       /* Remember the number of running sets */
+       return rp + 1;
+}
+
+/*
+ * If o->color_moved is COLOR_MOVED_PLAIN, this function does nothing.
+ *
+ * Otherwise, if the last block has fewer alphanumeric characters than
+ * COLOR_MOVED_MIN_ALNUM_COUNT, unset DIFF_SYMBOL_MOVED_LINE on all lines in
+ * that block.
+ *
+ * The last block consists of the (n - block_length)'th line up to but not
+ * including the nth line.
+ *
+ * NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
+ * Think of a way to unify them.
+ */
+static void adjust_last_block(struct diff_options *o, int n, int block_length)
+{
+       int i, alnum_count = 0;
+       if (o->color_moved == COLOR_MOVED_PLAIN)
+               return;
+       for (i = 1; i < block_length + 1; i++) {
+               const char *c = o->emitted_symbols->buf[n - i].line;
+               for (; *c; c++) {
+                       if (!isalnum(*c))
+                               continue;
+                       alnum_count++;
+                       if (alnum_count >= COLOR_MOVED_MIN_ALNUM_COUNT)
+                               return;
+               }
+       }
+       for (i = 1; i < block_length + 1; i++)
+               o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
+}
+
+/* Find blocks of moved code, delegate actual coloring decision to helper */
+static void mark_color_as_moved(struct diff_options *o,
+                               struct hashmap *add_lines,
+                               struct hashmap *del_lines)
+{
+       struct moved_entry **pmb = NULL; /* potentially moved blocks */
+       int pmb_nr = 0, pmb_alloc = 0;
+       int n, flipped_block = 1, block_length = 0;
+
+
+       for (n = 0; n < o->emitted_symbols->nr; n++) {
+               struct hashmap *hm = NULL;
+               struct moved_entry *key;
+               struct moved_entry *match = NULL;
+               struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+               int i;
+
+               switch (l->s) {
+               case DIFF_SYMBOL_PLUS:
+                       hm = del_lines;
+                       key = prepare_entry(o, n);
+                       match = hashmap_get(hm, key, o);
+                       free(key);
+                       break;
+               case DIFF_SYMBOL_MINUS:
+                       hm = add_lines;
+                       key = prepare_entry(o, n);
+                       match = hashmap_get(hm, key, o);
+                       free(key);
+                       break;
+               default:
+                       flipped_block = 1;
+               }
+
+               if (!match) {
+                       adjust_last_block(o, n, block_length);
+                       pmb_nr = 0;
+                       block_length = 0;
+                       continue;
+               }
+
+               l->flags |= DIFF_SYMBOL_MOVED_LINE;
+
+               if (o->color_moved == COLOR_MOVED_PLAIN)
+                       continue;
+
+               /* Check any potential block runs, advance each or nullify */
+               for (i = 0; i < pmb_nr; i++) {
+                       struct moved_entry *p = pmb[i];
+                       struct moved_entry *pnext = (p && p->next_line) ?
+                                       p->next_line : NULL;
+                       if (pnext && !hm->cmpfn(o, pnext, match, NULL)) {
+                               pmb[i] = p->next_line;
+                       } else {
+                               pmb[i] = NULL;
+                       }
+               }
+
+               pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr);
+
+               if (pmb_nr == 0) {
+                       /*
+                        * The current line is the start of a new block.
+                        * Setup the set of potential blocks.
+                        */
+                       for (; match; match = hashmap_get_next(hm, match)) {
+                               ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
+                               pmb[pmb_nr++] = match;
+                       }
+
+                       flipped_block = (flipped_block + 1) % 2;
+
+                       adjust_last_block(o, n, block_length);
+                       block_length = 0;
+               }
+
+               block_length++;
+
+               if (flipped_block)
+                       l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
+       }
+       adjust_last_block(o, n, block_length);
+
+       free(pmb);
+}
+
+#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
+  (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
+static void dim_moved_lines(struct diff_options *o)
+{
+       int n;
+       for (n = 0; n < o->emitted_symbols->nr; n++) {
+               struct emitted_diff_symbol *prev = (n != 0) ?
+                               &o->emitted_symbols->buf[n - 1] : NULL;
+               struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+               struct emitted_diff_symbol *next =
+                               (n < o->emitted_symbols->nr - 1) ?
+                               &o->emitted_symbols->buf[n + 1] : NULL;
+
+               /* Not a plus or minus line? */
+               if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS)
+                       continue;
+
+               /* Not a moved line? */
+               if (!(l->flags & DIFF_SYMBOL_MOVED_LINE))
+                       continue;
+
+               /*
+                * If prev or next are not a plus or minus line,
+                * pretend they don't exist
+                */
+               if (prev && prev->s != DIFF_SYMBOL_PLUS &&
+                           prev->s != DIFF_SYMBOL_MINUS)
+                       prev = NULL;
+               if (next && next->s != DIFF_SYMBOL_PLUS &&
+                           next->s != DIFF_SYMBOL_MINUS)
+                       next = NULL;
+
+               /* Inside a block? */
+               if ((prev &&
+                   (prev->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) ==
+                   (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK)) &&
+                   (next &&
+                   (next->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) ==
+                   (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK))) {
+                       l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING;
+                       continue;
+               }
+
+               /* Check if we are at an interesting bound: */
+               if (prev && (prev->flags & DIFF_SYMBOL_MOVED_LINE) &&
+                   (prev->flags & DIFF_SYMBOL_MOVED_LINE_ALT) !=
+                      (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT))
+                       continue;
+               if (next && (next->flags & DIFF_SYMBOL_MOVED_LINE) &&
+                   (next->flags & DIFF_SYMBOL_MOVED_LINE_ALT) !=
+                      (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT))
+                       continue;
+
+               /*
+                * The boundary to prev and next are not interesting,
+                * so this line is not interesting as a whole
+                */
+               l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING;
+       }
+}
+
+static void emit_line_ws_markup(struct diff_options *o,
+                               const char *set, const char *reset,
+                               const char *line, int len, char sign,
+                               unsigned ws_rule, int blank_at_eof)
 {
-       const char *set = diff_get_color(ecbdata->color_diff, color);
        const char *ws = NULL;
 
-       if (ecbdata->opt->ws_error_highlight & ws_error_highlight) {
-               ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
+       if (o->ws_error_highlight & ws_rule) {
+               ws = diff_get_color_opt(o, DIFF_WHITESPACE);
                if (!*ws)
                        ws = NULL;
        }
 
        if (!ws)
-               emit_line_0(ecbdata->opt, set, reset, sign, line, len);
-       else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len))
+               emit_line_0(o, set, reset, sign, line, len);
+       else if (blank_at_eof)
                /* Blank line at EOF - paint '+' as well */
-               emit_line_0(ecbdata->opt, ws, reset, sign, line, len);
+               emit_line_0(o, ws, reset, sign, line, len);
        else {
                /* Emit just the prefix, then the rest. */
-               emit_line_0(ecbdata->opt, set, reset, sign, "", 0);
-               ws_check_emit(line, len, ecbdata->ws_rule,
-                             ecbdata->opt->file, set, reset, ws);
+               emit_line_0(o, set, reset, sign, "", 0);
+               ws_check_emit(line, len, ws_rule,
+                             o->file, set, reset, ws);
+       }
+}
+
+static void emit_diff_symbol_from_struct(struct diff_options *o,
+                                        struct emitted_diff_symbol *eds)
+{
+       static const char *nneof = " No newline at end of file\n";
+       const char *context, *reset, *set, *meta, *fraginfo;
+       struct strbuf sb = STRBUF_INIT;
+
+       enum diff_symbol s = eds->s;
+       const char *line = eds->line;
+       int len = eds->len;
+       unsigned flags = eds->flags;
+
+       switch (s) {
+       case DIFF_SYMBOL_NO_LF_EOF:
+               context = diff_get_color_opt(o, DIFF_CONTEXT);
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               putc('\n', o->file);
+               emit_line_0(o, context, reset, '\\',
+                           nneof, strlen(nneof));
+               break;
+       case DIFF_SYMBOL_SUBMODULE_HEADER:
+       case DIFF_SYMBOL_SUBMODULE_ERROR:
+       case DIFF_SYMBOL_SUBMODULE_PIPETHROUGH:
+       case DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES:
+       case DIFF_SYMBOL_SUMMARY:
+       case DIFF_SYMBOL_STATS_LINE:
+       case DIFF_SYMBOL_BINARY_DIFF_BODY:
+       case DIFF_SYMBOL_CONTEXT_FRAGINFO:
+               emit_line(o, "", "", line, len);
+               break;
+       case DIFF_SYMBOL_CONTEXT_INCOMPLETE:
+       case DIFF_SYMBOL_CONTEXT_MARKER:
+               context = diff_get_color_opt(o, DIFF_CONTEXT);
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               emit_line(o, context, reset, line, len);
+               break;
+       case DIFF_SYMBOL_SEPARATOR:
+               fprintf(o->file, "%s%c",
+                       diff_line_prefix(o),
+                       o->line_termination);
+               break;
+       case DIFF_SYMBOL_CONTEXT:
+               set = diff_get_color_opt(o, DIFF_CONTEXT);
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               emit_line_ws_markup(o, set, reset, line, len, ' ',
+                                   flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
+               break;
+       case DIFF_SYMBOL_PLUS:
+               switch (flags & (DIFF_SYMBOL_MOVED_LINE |
+                                DIFF_SYMBOL_MOVED_LINE_ALT |
+                                DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) {
+               case DIFF_SYMBOL_MOVED_LINE |
+                    DIFF_SYMBOL_MOVED_LINE_ALT |
+                    DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+                       set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT_DIM);
+                       break;
+               case DIFF_SYMBOL_MOVED_LINE |
+                    DIFF_SYMBOL_MOVED_LINE_ALT:
+                       set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT);
+                       break;
+               case DIFF_SYMBOL_MOVED_LINE |
+                    DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+                       set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_DIM);
+                       break;
+               case DIFF_SYMBOL_MOVED_LINE:
+                       set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED);
+                       break;
+               default:
+                       set = diff_get_color_opt(o, DIFF_FILE_NEW);
+               }
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               emit_line_ws_markup(o, set, reset, line, len, '+',
+                                   flags & DIFF_SYMBOL_CONTENT_WS_MASK,
+                                   flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
+               break;
+       case DIFF_SYMBOL_MINUS:
+               switch (flags & (DIFF_SYMBOL_MOVED_LINE |
+                                DIFF_SYMBOL_MOVED_LINE_ALT |
+                                DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) {
+               case DIFF_SYMBOL_MOVED_LINE |
+                    DIFF_SYMBOL_MOVED_LINE_ALT |
+                    DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+                       set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT_DIM);
+                       break;
+               case DIFF_SYMBOL_MOVED_LINE |
+                    DIFF_SYMBOL_MOVED_LINE_ALT:
+                       set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT);
+                       break;
+               case DIFF_SYMBOL_MOVED_LINE |
+                    DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
+                       set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_DIM);
+                       break;
+               case DIFF_SYMBOL_MOVED_LINE:
+                       set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED);
+                       break;
+               default:
+                       set = diff_get_color_opt(o, DIFF_FILE_OLD);
+               }
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               emit_line_ws_markup(o, set, reset, line, len, '-',
+                                   flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
+               break;
+       case DIFF_SYMBOL_WORDS_PORCELAIN:
+               context = diff_get_color_opt(o, DIFF_CONTEXT);
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               emit_line(o, context, reset, line, len);
+               fputs("~\n", o->file);
+               break;
+       case DIFF_SYMBOL_WORDS:
+               context = diff_get_color_opt(o, DIFF_CONTEXT);
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               /*
+                * Skip the prefix character, if any.  With
+                * diff_suppress_blank_empty, there may be
+                * none.
+                */
+               if (line[0] != '\n') {
+                       line++;
+                       len--;
+               }
+               emit_line(o, context, reset, line, len);
+               break;
+       case DIFF_SYMBOL_FILEPAIR_PLUS:
+               meta = diff_get_color_opt(o, DIFF_METAINFO);
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               fprintf(o->file, "%s%s+++ %s%s%s\n", diff_line_prefix(o), meta,
+                       line, reset,
+                       strchr(line, ' ') ? "\t" : "");
+               break;
+       case DIFF_SYMBOL_FILEPAIR_MINUS:
+               meta = diff_get_color_opt(o, DIFF_METAINFO);
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               fprintf(o->file, "%s%s--- %s%s%s\n", diff_line_prefix(o), meta,
+                       line, reset,
+                       strchr(line, ' ') ? "\t" : "");
+               break;
+       case DIFF_SYMBOL_BINARY_FILES:
+       case DIFF_SYMBOL_HEADER:
+               fprintf(o->file, "%s", line);
+               break;
+       case DIFF_SYMBOL_BINARY_DIFF_HEADER:
+               fprintf(o->file, "%sGIT binary patch\n", diff_line_prefix(o));
+               break;
+       case DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA:
+               fprintf(o->file, "%sdelta %s\n", diff_line_prefix(o), line);
+               break;
+       case DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL:
+               fprintf(o->file, "%sliteral %s\n", diff_line_prefix(o), line);
+               break;
+       case DIFF_SYMBOL_BINARY_DIFF_FOOTER:
+               fputs(diff_line_prefix(o), o->file);
+               fputc('\n', o->file);
+               break;
+       case DIFF_SYMBOL_REWRITE_DIFF:
+               fraginfo = diff_get_color(o->use_color, DIFF_FRAGINFO);
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               emit_line(o, fraginfo, reset, line, len);
+               break;
+       case DIFF_SYMBOL_SUBMODULE_ADD:
+               set = diff_get_color_opt(o, DIFF_FILE_NEW);
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               emit_line(o, set, reset, line, len);
+               break;
+       case DIFF_SYMBOL_SUBMODULE_DEL:
+               set = diff_get_color_opt(o, DIFF_FILE_OLD);
+               reset = diff_get_color_opt(o, DIFF_RESET);
+               emit_line(o, set, reset, line, len);
+               break;
+       case DIFF_SYMBOL_SUBMODULE_UNTRACKED:
+               fprintf(o->file, "%sSubmodule %s contains untracked content\n",
+                       diff_line_prefix(o), line);
+               break;
+       case DIFF_SYMBOL_SUBMODULE_MODIFIED:
+               fprintf(o->file, "%sSubmodule %s contains modified content\n",
+                       diff_line_prefix(o), line);
+               break;
+       case DIFF_SYMBOL_STATS_SUMMARY_NO_FILES:
+               emit_line(o, "", "", " 0 files changed\n",
+                         strlen(" 0 files changed\n"));
+               break;
+       case DIFF_SYMBOL_STATS_SUMMARY_ABBREV:
+               emit_line(o, "", "", " ...\n", strlen(" ...\n"));
+               break;
+       case DIFF_SYMBOL_WORD_DIFF:
+               fprintf(o->file, "%.*s", len, line);
+               break;
+       case DIFF_SYMBOL_STAT_SEP:
+               fputs(o->stat_sep, o->file);
+               break;
+       default:
+               die("BUG: unknown diff symbol");
        }
+       strbuf_release(&sb);
+}
+
+static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
+                            const char *line, int len, unsigned flags)
+{
+       struct emitted_diff_symbol e = {line, len, flags, s};
+
+       if (o->emitted_symbols)
+               append_emitted_diff_symbol(o, &e);
+       else
+               emit_diff_symbol_from_struct(o, &e);
+}
+
+void diff_emit_submodule_del(struct diff_options *o, const char *line)
+{
+       emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_DEL, line, strlen(line), 0);
+}
+
+void diff_emit_submodule_add(struct diff_options *o, const char *line)
+{
+       emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_ADD, line, strlen(line), 0);
+}
+
+void diff_emit_submodule_untracked(struct diff_options *o, const char *path)
+{
+       emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_UNTRACKED,
+                        path, strlen(path), 0);
+}
+
+void diff_emit_submodule_modified(struct diff_options *o, const char *path)
+{
+       emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_MODIFIED,
+                        path, strlen(path), 0);
+}
+
+void diff_emit_submodule_header(struct diff_options *o, const char *header)
+{
+       emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_HEADER,
+                        header, strlen(header), 0);
+}
+
+void diff_emit_submodule_error(struct diff_options *o, const char *err)
+{
+       emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_ERROR, err, strlen(err), 0);
+}
+
+void diff_emit_submodule_pipethrough(struct diff_options *o,
+                                    const char *line, int len)
+{
+       emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_PIPETHROUGH, line, len, 0);
+}
+
+static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
+{
+       if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
+             ecbdata->blank_at_eof_in_preimage &&
+             ecbdata->blank_at_eof_in_postimage &&
+             ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
+             ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
+               return 0;
+       return ws_blank_line(line, len, ecbdata->ws_rule);
 }
 
 static void emit_add_line(const char *reset,
                          struct emit_callback *ecbdata,
                          const char *line, int len)
 {
-       emit_line_checked(reset, ecbdata, line, len,
-                         DIFF_FILE_NEW, WSEH_NEW, '+');
+       unsigned flags = WSEH_NEW | ecbdata->ws_rule;
+       if (new_blank_line_at_eof(ecbdata, line, len))
+               flags |= DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF;
+
+       emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_PLUS, line, len, flags);
 }
 
 static void emit_del_line(const char *reset,
                          struct emit_callback *ecbdata,
                          const char *line, int len)
 {
-       emit_line_checked(reset, ecbdata, line, len,
-                         DIFF_FILE_OLD, WSEH_OLD, '-');
+       unsigned flags = WSEH_OLD | ecbdata->ws_rule;
+       emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_MINUS, line, len, flags);
 }
 
 static void emit_context_line(const char *reset,
                              struct emit_callback *ecbdata,
                              const char *line, int len)
 {
-       emit_line_checked(reset, ecbdata, line, len,
-                         DIFF_CONTEXT, WSEH_CONTEXT, ' ');
+       unsigned flags = WSEH_CONTEXT | ecbdata->ws_rule;
+       emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_CONTEXT, line, len, flags);
 }
 
 static void emit_hunk_header(struct emit_callback *ecbdata,
@@ -639,7 +1361,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
        if (len < 10 ||
            memcmp(line, atat, 2) ||
            !(ep = memmem(line + 2, len - 2, atat, 2))) {
-               emit_line(ecbdata->opt, context, reset, line, len);
+               emit_diff_symbol(ecbdata->opt,
+                                DIFF_SYMBOL_CONTEXT_MARKER, line, len, 0);
                return;
        }
        ep += 2; /* skip over @@ */
@@ -673,7 +1396,9 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
        }
 
        strbuf_add(&msgbuf, line + len, org_len - len);
-       emit_line(ecbdata->opt, "", "", msgbuf.buf, msgbuf.len);
+       strbuf_complete_line(&msgbuf);
+       emit_diff_symbol(ecbdata->opt,
+                        DIFF_SYMBOL_CONTEXT_FRAGINFO, msgbuf.buf, msgbuf.len, 0);
        strbuf_release(&msgbuf);
 }
 
@@ -689,23 +1414,23 @@ static void remove_tempfile(void)
 {
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
-               if (is_tempfile_active(&diff_temp[i].tempfile))
+               if (is_tempfile_active(diff_temp[i].tempfile))
                        delete_tempfile(&diff_temp[i].tempfile);
                diff_temp[i].name = NULL;
        }
 }
 
-static void print_line_count(FILE *file, int count)
+static void add_line_count(struct strbuf *out, int count)
 {
        switch (count) {
        case 0:
-               fprintf(file, "0,0");
+               strbuf_addstr(out, "0,0");
                break;
        case 1:
-               fprintf(file, "1");
+               strbuf_addstr(out, "1");
                break;
        default:
-               fprintf(file, "1,%d", count);
+               strbuf_addf(out, "1,%d", count);
                break;
        }
 }
@@ -714,7 +1439,6 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
                               int prefix, const char *data, int size)
 {
        const char *endp = NULL;
-       static const char *nneof = " No newline at end of file\n";
        const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
 
        while (0 < size) {
@@ -732,13 +1456,8 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
                size -= len;
                data += len;
        }
-       if (!endp) {
-               const char *context = diff_get_color(ecb->color_diff,
-                                                    DIFF_CONTEXT);
-               putc('\n', ecb->opt->file);
-               emit_line_0(ecb->opt, context, reset, '\\',
-                           nneof, strlen(nneof));
-       }
+       if (!endp)
+               emit_diff_symbol(ecb->opt, DIFF_SYMBOL_NO_LF_EOF, NULL, 0, 0);
 }
 
 static void emit_rewrite_diff(const char *name_a,
@@ -750,16 +1469,12 @@ static void emit_rewrite_diff(const char *name_a,
                              struct diff_options *o)
 {
        int lc_a, lc_b;
-       const char *name_a_tab, *name_b_tab;
-       const char *metainfo = diff_get_color(o->use_color, DIFF_METAINFO);
-       const char *fraginfo = diff_get_color(o->use_color, DIFF_FRAGINFO);
-       const char *reset = diff_get_color(o->use_color, DIFF_RESET);
        static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
        const char *a_prefix, *b_prefix;
        char *data_one, *data_two;
        size_t size_one, size_two;
        struct emit_callback ecbdata;
-       const char *line_prefix = diff_line_prefix(o);
+       struct strbuf out = STRBUF_INIT;
 
        if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
                a_prefix = o->b_prefix;
@@ -771,8 +1486,6 @@ static void emit_rewrite_diff(const char *name_a,
 
        name_a += (*name_a == '/');
        name_b += (*name_b == '/');
-       name_a_tab = strchr(name_a, ' ') ? "\t" : "";
-       name_b_tab = strchr(name_b, ' ') ? "\t" : "";
 
        strbuf_reset(&a_name);
        strbuf_reset(&b_name);
@@ -799,18 +1512,23 @@ static void emit_rewrite_diff(const char *name_a,
 
        lc_a = count_lines(data_one, size_one);
        lc_b = count_lines(data_two, size_two);
-       fprintf(o->file,
-               "%s%s--- %s%s%s\n%s%s+++ %s%s%s\n%s%s@@ -",
-               line_prefix, metainfo, a_name.buf, name_a_tab, reset,
-               line_prefix, metainfo, b_name.buf, name_b_tab, reset,
-               line_prefix, fraginfo);
+
+       emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_MINUS,
+                        a_name.buf, a_name.len, 0);
+       emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_PLUS,
+                        b_name.buf, b_name.len, 0);
+
+       strbuf_addstr(&out, "@@ -");
        if (!o->irreversible_delete)
-               print_line_count(o->file, lc_a);
+               add_line_count(&out, lc_a);
        else
-               fprintf(o->file, "?,?");
-       fprintf(o->file, " +");
-       print_line_count(o->file, lc_b);
-       fprintf(o->file, " @@%s\n", reset);
+               strbuf_addstr(&out, "?,?");
+       strbuf_addstr(&out, " +");
+       add_line_count(&out, lc_b);
+       strbuf_addstr(&out, " @@\n");
+       emit_diff_symbol(o, DIFF_SYMBOL_REWRITE_DIFF, out.buf, out.len, 0);
+       strbuf_release(&out);
+
        if (lc_a && !o->irreversible_delete)
                emit_rewrite_lines(&ecbdata, '-', data_one, size_one);
        if (lc_b)
@@ -870,37 +1588,49 @@ struct diff_words_data {
        struct diff_words_style *style;
 };
 
-static int fn_out_diff_words_write_helper(FILE *fp,
+static int fn_out_diff_words_write_helper(struct diff_options *o,
                                          struct diff_words_style_elem *st_el,
                                          const char *newline,
-                                         size_t count, const char *buf,
-                                         const char *line_prefix)
+                                         size_t count, const char *buf)
 {
        int print = 0;
+       struct strbuf sb = STRBUF_INIT;
 
        while (count) {
                char *p = memchr(buf, '\n', count);
                if (print)
-                       fputs(line_prefix, fp);
+                       strbuf_addstr(&sb, diff_line_prefix(o));
+
                if (p != buf) {
-                       if (st_el->color && fputs(st_el->color, fp) < 0)
-                               return -1;
-                       if (fputs(st_el->prefix, fp) < 0 ||
-                           fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
-                           fputs(st_el->suffix, fp) < 0)
-                               return -1;
-                       if (st_el->color && *st_el->color
-                           && fputs(GIT_COLOR_RESET, fp) < 0)
-                               return -1;
+                       const char *reset = st_el->color && *st_el->color ?
+                                           GIT_COLOR_RESET : NULL;
+                       if (st_el->color && *st_el->color)
+                               strbuf_addstr(&sb, st_el->color);
+                       strbuf_addstr(&sb, st_el->prefix);
+                       strbuf_add(&sb, buf, p ? p - buf : count);
+                       strbuf_addstr(&sb, st_el->suffix);
+                       if (reset)
+                               strbuf_addstr(&sb, reset);
                }
                if (!p)
-                       return 0;
-               if (fputs(newline, fp) < 0)
-                       return -1;
+                       goto out;
+
+               strbuf_addstr(&sb, newline);
                count -= p + 1 - buf;
                buf = p + 1;
                print = 1;
+               if (count) {
+                       emit_diff_symbol(o, DIFF_SYMBOL_WORD_DIFF,
+                                        sb.buf, sb.len, 0);
+                       strbuf_reset(&sb);
+               }
        }
+
+out:
+       if (sb.len)
+               emit_diff_symbol(o, DIFF_SYMBOL_WORD_DIFF,
+                                sb.buf, sb.len, 0);
+       strbuf_release(&sb);
        return 0;
 }
 
@@ -982,24 +1712,20 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
                fputs(line_prefix, diff_words->opt->file);
        }
        if (diff_words->current_plus != plus_begin) {
-               fn_out_diff_words_write_helper(diff_words->opt->file,
+               fn_out_diff_words_write_helper(diff_words->opt,
                                &style->ctx, style->newline,
                                plus_begin - diff_words->current_plus,
-                               diff_words->current_plus, line_prefix);
-               if (*(plus_begin - 1) == '\n')
-                       fputs(line_prefix, diff_words->opt->file);
+                               diff_words->current_plus);
        }
        if (minus_begin != minus_end) {
-               fn_out_diff_words_write_helper(diff_words->opt->file,
+               fn_out_diff_words_write_helper(diff_words->opt,
                                &style->old, style->newline,
-                               minus_end - minus_begin, minus_begin,
-                               line_prefix);
+                               minus_end - minus_begin, minus_begin);
        }
        if (plus_begin != plus_end) {
-               fn_out_diff_words_write_helper(diff_words->opt->file,
+               fn_out_diff_words_write_helper(diff_words->opt,
                                &style->new, style->newline,
-                               plus_end - plus_begin, plus_begin,
-                               line_prefix);
+                               plus_end - plus_begin, plus_begin);
        }
 
        diff_words->current_plus = plus_end;
@@ -1093,11 +1819,12 @@ static void diff_words_show(struct diff_words_data *diff_words)
 
        /* special case: only removal */
        if (!diff_words->plus.text.size) {
-               fputs(line_prefix, diff_words->opt->file);
-               fn_out_diff_words_write_helper(diff_words->opt->file,
+               emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF,
+                                line_prefix, strlen(line_prefix), 0);
+               fn_out_diff_words_write_helper(diff_words->opt,
                        &style->old, style->newline,
                        diff_words->minus.text.size,
-                       diff_words->minus.text.ptr, line_prefix);
+                       diff_words->minus.text.ptr);
                diff_words->minus.text.size = 0;
                return;
        }
@@ -1120,12 +1847,12 @@ static void diff_words_show(struct diff_words_data *diff_words)
        if (diff_words->current_plus != diff_words->plus.text.ptr +
                        diff_words->plus.text.size) {
                if (color_words_output_graph_prefix(diff_words))
-                       fputs(line_prefix, diff_words->opt->file);
-               fn_out_diff_words_write_helper(diff_words->opt->file,
+                       emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF,
+                                        line_prefix, strlen(line_prefix), 0);
+               fn_out_diff_words_write_helper(diff_words->opt,
                        &style->ctx, style->newline,
                        diff_words->plus.text.ptr + diff_words->plus.text.size
-                       - diff_words->current_plus, diff_words->current_plus,
-                       line_prefix);
+                       - diff_words->current_plus, diff_words->current_plus);
        }
        diff_words->minus.text.size = diff_words->plus.text.size = 0;
 }
@@ -1133,9 +1860,29 @@ static void diff_words_show(struct diff_words_data *diff_words)
 /* In "color-words" mode, show word-diff of words accumulated in the buffer */
 static void diff_words_flush(struct emit_callback *ecbdata)
 {
+       struct diff_options *wo = ecbdata->diff_words->opt;
+
        if (ecbdata->diff_words->minus.text.size ||
            ecbdata->diff_words->plus.text.size)
                diff_words_show(ecbdata->diff_words);
+
+       if (wo->emitted_symbols) {
+               struct diff_options *o = ecbdata->opt;
+               struct emitted_diff_symbols *wol = wo->emitted_symbols;
+               int i;
+
+               /*
+                * NEEDSWORK:
+                * Instead of appending each, concat all words to a line?
+                */
+               for (i = 0; i < wol->nr; i++)
+                       append_emitted_diff_symbol(o, &wol->buf[i]);
+
+               for (i = 0; i < wol->nr; i++)
+                       free((void *)wol->buf[i].line);
+
+               wol->nr = 0;
+       }
 }
 
 static void diff_filespec_load_driver(struct diff_filespec *one)
@@ -1171,6 +1918,11 @@ static void init_diff_words_data(struct emit_callback *ecbdata,
                xcalloc(1, sizeof(struct diff_words_data));
        ecbdata->diff_words->type = o->word_diff;
        ecbdata->diff_words->opt = o;
+
+       if (orig_opts->emitted_symbols)
+               o->emitted_symbols =
+                       xcalloc(1, sizeof(struct emitted_diff_symbols));
+
        if (!o->word_regex)
                o->word_regex = userdiff_word_regex(one);
        if (!o->word_regex)
@@ -1205,6 +1957,7 @@ static void free_diff_words_data(struct emit_callback *ecbdata)
 {
        if (ecbdata->diff_words) {
                diff_words_flush(ecbdata);
+               free (ecbdata->diff_words->opt->emitted_symbols);
                free (ecbdata->diff_words->opt);
                free (ecbdata->diff_words->minus.text.ptr);
                free (ecbdata->diff_words->minus.orig);
@@ -1269,30 +2022,25 @@ static void find_lno(const char *line, struct emit_callback *ecbdata)
 static void fn_out_consume(void *priv, char *line, unsigned long len)
 {
        struct emit_callback *ecbdata = priv;
-       const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
-       const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
        struct diff_options *o = ecbdata->opt;
-       const char *line_prefix = diff_line_prefix(o);
 
        o->found_changes = 1;
 
        if (ecbdata->header) {
-               fprintf(o->file, "%s", ecbdata->header->buf);
+               emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+                                ecbdata->header->buf, ecbdata->header->len, 0);
                strbuf_reset(ecbdata->header);
                ecbdata->header = NULL;
        }
 
        if (ecbdata->label_path[0]) {
-               const char *name_a_tab, *name_b_tab;
-
-               name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
-               name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
-
-               fprintf(o->file, "%s%s--- %s%s%s\n",
-                       line_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);
-               fprintf(o->file, "%s%s+++ %s%s%s\n",
-                       line_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);
+               emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_MINUS,
+                                ecbdata->label_path[0],
+                                strlen(ecbdata->label_path[0]), 0);
+               emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_PLUS,
+                                ecbdata->label_path[1],
+                                strlen(ecbdata->label_path[1]), 0);
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
 
@@ -1308,12 +2056,13 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                len = sane_truncate_line(ecbdata, line, len);
                find_lno(line, ecbdata);
                emit_hunk_header(ecbdata, line, len);
-               if (line[len-1] != '\n')
-                       putc('\n', o->file);
                return;
        }
 
        if (ecbdata->diff_words) {
+               enum diff_symbol s =
+                       ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN ?
+                       DIFF_SYMBOL_WORDS_PORCELAIN : DIFF_SYMBOL_WORDS;
                if (line[0] == '-') {
                        diff_words_append(line, len,
                                          &ecbdata->diff_words->minus);
@@ -1333,21 +2082,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                        return;
                }
                diff_words_flush(ecbdata);
-               if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
-                       emit_line(o, context, reset, line, len);
-                       fputs("~\n", o->file);
-               } else {
-                       /*
-                        * Skip the prefix character, if any.  With
-                        * diff_suppress_blank_empty, there may be
-                        * none.
-                        */
-                       if (line[0] != '\n') {
-                             line++;
-                             len--;
-                       }
-                       emit_line(o, context, reset, line, len);
-               }
+               emit_diff_symbol(o, s, line, len, 0);
                return;
        }
 
@@ -1368,8 +2103,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        default:
                /* incomplete line at the end */
                ecbdata->lno_in_preimage++;
-               emit_line(o, diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
-                         reset, line, len);
+               emit_diff_symbol(o, DIFF_SYMBOL_CONTEXT_INCOMPLETE,
+                                line, len, 0);
                break;
        }
 }
@@ -1514,20 +2249,14 @@ static int scale_linear(int it, int width, int max_change)
        return 1 + (it * (width - 1) / max_change);
 }
 
-static void show_name(FILE *file,
-                     const char *prefix, const char *name, int len)
-{
-       fprintf(file, " %s%-*s |", prefix, len, name);
-}
-
-static void show_graph(FILE *file, char ch, int cnt, const char *set, const char *reset)
+static void show_graph(struct strbuf *out, char ch, int cnt,
+                      const char *set, const char *reset)
 {
        if (cnt <= 0)
                return;
-       fprintf(file, "%s", set);
-       while (cnt--)
-               putc(ch, file);
-       fprintf(file, "%s", reset);
+       strbuf_addstr(out, set);
+       strbuf_addchars(out, ch, cnt);
+       strbuf_addstr(out, reset);
 }
 
 static void fill_print_name(struct diffstat_file *file)
@@ -1551,14 +2280,16 @@ static void fill_print_name(struct diffstat_file *file)
        file->print_name = pname;
 }
 
-int print_stat_summary(FILE *fp, int files, int insertions, int deletions)
+static void print_stat_summary_inserts_deletes(struct diff_options *options,
+               int files, int insertions, int deletions)
 {
        struct strbuf sb = STRBUF_INIT;
-       int ret;
 
        if (!files) {
                assert(insertions == 0 && deletions == 0);
-               return fprintf(fp, "%s\n", " 0 files changed");
+               emit_diff_symbol(options, DIFF_SYMBOL_STATS_SUMMARY_NO_FILES,
+                                NULL, 0, 0);
+               return;
        }
 
        strbuf_addf(&sb,
@@ -1585,9 +2316,19 @@ int print_stat_summary(FILE *fp, int files, int insertions, int deletions)
                            deletions);
        }
        strbuf_addch(&sb, '\n');
-       ret = fputs(sb.buf, fp);
+       emit_diff_symbol(options, DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES,
+                        sb.buf, sb.len, 0);
        strbuf_release(&sb);
-       return ret;
+}
+
+void print_stat_summary(FILE *fp, int files,
+                       int insertions, int deletions)
+{
+       struct diff_options o;
+       memset(&o, 0, sizeof(o));
+       o.file = fp;
+
+       print_stat_summary_inserts_deletes(&o, files, insertions, deletions);
 }
 
 static void show_stats(struct diffstat_t *data, struct diff_options *options)
@@ -1597,13 +2338,13 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
        int total_files = data->nr, count;
        int width, name_width, graph_width, number_width = 0, bin_width = 0;
        const char *reset, *add_c, *del_c;
-       const char *line_prefix = "";
        int extra_shown = 0;
+       const char *line_prefix = diff_line_prefix(options);
+       struct strbuf out = STRBUF_INIT;
 
        if (data->nr == 0)
                return;
 
-       line_prefix = diff_line_prefix(options);
        count = options->stat_count ? options->stat_count : data->nr;
 
        reset = diff_get_color_opt(options, DIFF_RESET);
@@ -1757,26 +2498,32 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                }
 
                if (file->is_binary) {
-                       fprintf(options->file, "%s", line_prefix);
-                       show_name(options->file, prefix, name, len);
-                       fprintf(options->file, " %*s", number_width, "Bin");
+                       strbuf_addf(&out, " %s%-*s |", prefix, len, name);
+                       strbuf_addf(&out, " %*s", number_width, "Bin");
                        if (!added && !deleted) {
-                               putc('\n', options->file);
+                               strbuf_addch(&out, '\n');
+                               emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
+                                                out.buf, out.len, 0);
+                               strbuf_reset(&out);
                                continue;
                        }
-                       fprintf(options->file, " %s%"PRIuMAX"%s",
+                       strbuf_addf(&out, " %s%"PRIuMAX"%s",
                                del_c, deleted, reset);
-                       fprintf(options->file, " -> ");
-                       fprintf(options->file, "%s%"PRIuMAX"%s",
+                       strbuf_addstr(&out, " -> ");
+                       strbuf_addf(&out, "%s%"PRIuMAX"%s",
                                add_c, added, reset);
-                       fprintf(options->file, " bytes");
-                       fprintf(options->file, "\n");
+                       strbuf_addstr(&out, " bytes\n");
+                       emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
+                                        out.buf, out.len, 0);
+                       strbuf_reset(&out);
                        continue;
                }
                else if (file->is_unmerged) {
-                       fprintf(options->file, "%s", line_prefix);
-                       show_name(options->file, prefix, name, len);
-                       fprintf(options->file, " Unmerged\n");
+                       strbuf_addf(&out, " %s%-*s |", prefix, len, name);
+                       strbuf_addstr(&out, " Unmerged\n");
+                       emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
+                                        out.buf, out.len, 0);
+                       strbuf_reset(&out);
                        continue;
                }
 
@@ -1799,14 +2546,16 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                                add = total - del;
                        }
                }
-               fprintf(options->file, "%s", line_prefix);
-               show_name(options->file, prefix, name, len);
-               fprintf(options->file, " %*"PRIuMAX"%s",
+               strbuf_addf(&out, " %s%-*s |", prefix, len, name);
+               strbuf_addf(&out, " %*"PRIuMAX"%s",
                        number_width, added + deleted,
                        added + deleted ? " " : "");
-               show_graph(options->file, '+', add, add_c, reset);
-               show_graph(options->file, '-', del, del_c, reset);
-               fprintf(options->file, "\n");
+               show_graph(&out, '+', add, add_c, reset);
+               show_graph(&out, '-', del, del_c, reset);
+               strbuf_addch(&out, '\n');
+               emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
+                                out.buf, out.len, 0);
+               strbuf_reset(&out);
        }
 
        for (i = 0; i < data->nr; i++) {
@@ -1827,11 +2576,13 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                if (i < count)
                        continue;
                if (!extra_shown)
-                       fprintf(options->file, "%s ...\n", line_prefix);
+                       emit_diff_symbol(options,
+                                        DIFF_SYMBOL_STATS_SUMMARY_ABBREV,
+                                        NULL, 0, 0);
                extra_shown = 1;
        }
-       fprintf(options->file, "%s", line_prefix);
-       print_stat_summary(options->file, total_files, adds, dels);
+
+       print_stat_summary_inserts_deletes(options, total_files, adds, dels);
 }
 
 static void show_shortstats(struct diffstat_t *data, struct diff_options *options)
@@ -1843,7 +2594,7 @@ static void show_shortstats(struct diffstat_t *data, struct diff_options *option
 
        for (i = 0; i < data->nr; i++) {
                int added = data->files[i]->added;
-               int deleted= data->files[i]->deleted;
+               int deleted = data->files[i]->deleted;
 
                if (data->files[i]->is_unmerged ||
                    (!data->files[i]->is_interesting && (added + deleted == 0))) {
@@ -1853,8 +2604,7 @@ static void show_shortstats(struct diffstat_t *data, struct diff_options *option
                        dels += deleted;
                }
        }
-       fprintf(options->file, "%s", diff_line_prefix(options));
-       print_stat_summary(options->file, total_files, adds, dels);
+       print_stat_summary_inserts_deletes(options, total_files, adds, dels);
 }
 
 static void show_numstat(struct diffstat_t *data, struct diff_options *options)
@@ -2218,8 +2968,8 @@ static unsigned char *deflate_it(char *data,
        return deflated;
 }
 
-static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two,
-                                 const char *prefix)
+static void emit_binary_diff_body(struct diff_options *o,
+                                 mmfile_t *one, mmfile_t *two)
 {
        void *cp;
        void *delta;
@@ -2248,13 +2998,18 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two,
        }
 
        if (delta && delta_size < deflate_size) {
-               fprintf(file, "%sdelta %lu\n", prefix, orig_size);
+               char *s = xstrfmt("%lu", orig_size);
+               emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA,
+                                s, strlen(s), 0);
+               free(s);
                free(deflated);
                data = delta;
                data_size = delta_size;
-       }
-       else {
-               fprintf(file, "%sliteral %lu\n", prefix, two->size);
+       } else {
+               char *s = xstrfmt("%lu", two->size);
+               emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL,
+                                s, strlen(s), 0);
+               free(s);
                free(delta);
                data = deflated;
                data_size = deflate_size;
@@ -2263,8 +3018,9 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two,
        /* emit data encoded in base85 */
        cp = data;
        while (data_size) {
+               int len;
                int bytes = (52 < data_size) ? 52 : data_size;
-               char line[70];
+               char line[71];
                data_size -= bytes;
                if (bytes <= 26)
                        line[0] = bytes + 'A' - 1;
@@ -2272,20 +3028,24 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two,
                        line[0] = bytes - 26 + 'a' - 1;
                encode_85(line + 1, cp, bytes);
                cp = (char *) cp + bytes;
-               fprintf(file, "%s", prefix);
-               fputs(line, file);
-               fputc('\n', file);
+
+               len = strlen(line);
+               line[len++] = '\n';
+               line[len] = '\0';
+
+               emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_BODY,
+                                line, len, 0);
        }
-       fprintf(file, "%s\n", prefix);
+       emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_FOOTER, NULL, 0, 0);
        free(data);
 }
 
-static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two,
-                            const char *prefix)
+static void emit_binary_diff(struct diff_options *o,
+                            mmfile_t *one, mmfile_t *two)
 {
-       fprintf(file, "%sGIT binary patch\n", prefix);
-       emit_binary_diff_body(file, one, two, prefix);
-       emit_binary_diff_body(file, two, one, prefix);
+       emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER, NULL, 0, 0);
+       emit_binary_diff_body(o, one, two);
+       emit_binary_diff_body(o, two, one);
 }
 
 int diff_filespec_is_binary(struct diff_filespec *one)
@@ -2362,24 +3122,16 @@ static void builtin_diff(const char *name_a,
        if (o->submodule_format == DIFF_SUBMODULE_LOG &&
            (!one->mode || S_ISGITLINK(one->mode)) &&
            (!two->mode || S_ISGITLINK(two->mode))) {
-               const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
-               const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
-               show_submodule_summary(o->file, one->path ? one->path : two->path,
-                               line_prefix,
+               show_submodule_summary(o, one->path ? one->path : two->path,
                                &one->oid, &two->oid,
-                               two->dirty_submodule,
-                               meta, del, add, reset);
+                               two->dirty_submodule);
                return;
        } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
                   (!one->mode || S_ISGITLINK(one->mode)) &&
                   (!two->mode || S_ISGITLINK(two->mode))) {
-               const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
-               const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
-               show_submodule_inline_diff(o->file, one->path ? one->path : two->path,
-                               line_prefix,
+               show_submodule_inline_diff(o, one->path ? one->path : two->path,
                                &one->oid, &two->oid,
-                               two->dirty_submodule,
-                               meta, del, add, reset, o);
+                               two->dirty_submodule);
                return;
        }
 
@@ -2428,7 +3180,8 @@ static void builtin_diff(const char *name_a,
                if (complete_rewrite &&
                    (textconv_one || !diff_filespec_is_binary(one)) &&
                    (textconv_two || !diff_filespec_is_binary(two))) {
-                       fprintf(o->file, "%s", header.buf);
+                       emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+                                        header.buf, header.len, 0);
                        strbuf_reset(&header);
                        emit_rewrite_diff(name_a, name_b, one, two,
                                                textconv_one, textconv_two, o);
@@ -2438,23 +3191,31 @@ static void builtin_diff(const char *name_a,
        }
 
        if (o->irreversible_delete && lbl[1][0] == '/') {
-               fprintf(o->file, "%s", header.buf);
+               emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf,
+                                header.len, 0);
                strbuf_reset(&header);
                goto free_ab_and_return;
        } else if (!DIFF_OPT_TST(o, TEXT) &&
            ( (!textconv_one && diff_filespec_is_binary(one)) ||
              (!textconv_two && diff_filespec_is_binary(two)) )) {
+               struct strbuf sb = STRBUF_INIT;
                if (!one->data && !two->data &&
                    S_ISREG(one->mode) && S_ISREG(two->mode) &&
                    !DIFF_OPT_TST(o, BINARY)) {
                        if (!oidcmp(&one->oid, &two->oid)) {
                                if (must_show_header)
-                                       fprintf(o->file, "%s", header.buf);
+                                       emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+                                                        header.buf, header.len,
+                                                        0);
                                goto free_ab_and_return;
                        }
-                       fprintf(o->file, "%s", header.buf);
-                       fprintf(o->file, "%sBinary files %s and %s differ\n",
-                               line_prefix, lbl[0], lbl[1]);
+                       emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+                                        header.buf, header.len, 0);
+                       strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
+                                   diff_line_prefix(o), lbl[0], lbl[1]);
+                       emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES,
+                                        sb.buf, sb.len, 0);
+                       strbuf_release(&sb);
                        goto free_ab_and_return;
                }
                if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
@@ -2463,16 +3224,21 @@ static void builtin_diff(const char *name_a,
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size)) {
                        if (must_show_header)
-                               fprintf(o->file, "%s", header.buf);
+                               emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+                                                header.buf, header.len, 0);
                        goto free_ab_and_return;
                }
-               fprintf(o->file, "%s", header.buf);
+               emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf, header.len, 0);
                strbuf_reset(&header);
                if (DIFF_OPT_TST(o, BINARY))
-                       emit_binary_diff(o->file, &mf1, &mf2, line_prefix);
-               else
-                       fprintf(o->file, "%sBinary files %s and %s differ\n",
-                               line_prefix, lbl[0], lbl[1]);
+                       emit_binary_diff(o, &mf1, &mf2);
+               else {
+                       strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
+                                   diff_line_prefix(o), lbl[0], lbl[1]);
+                       emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES,
+                                        sb.buf, sb.len, 0);
+                       strbuf_release(&sb);
+               }
                o->found_changes = 1;
        } else {
                /* Crazy xdl interfaces.. */
@@ -2484,7 +3250,8 @@ static void builtin_diff(const char *name_a,
                const struct userdiff_funcname *pe;
 
                if (must_show_header) {
-                       fprintf(o->file, "%s", header.buf);
+                       emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
+                                        header.buf, header.len, 0);
                        strbuf_reset(&header);
                }
 
@@ -2953,7 +3720,6 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
                           const struct object_id *oid,
                           int mode)
 {
-       int fd;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf template = STRBUF_INIT;
        char *path_dup = xstrdup(path);
@@ -2963,18 +3729,18 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
        strbuf_addstr(&template, "XXXXXX_");
        strbuf_addstr(&template, base);
 
-       fd = mks_tempfile_ts(&temp->tempfile, template.buf, strlen(base) + 1);
-       if (fd < 0)
+       temp->tempfile = mks_tempfile_ts(template.buf, strlen(base) + 1);
+       if (!temp->tempfile)
                die_errno("unable to create temp-file");
        if (convert_to_working_tree(path,
                        (const char *)blob, (size_t)size, &buf)) {
                blob = buf.buf;
                size = buf.len;
        }
-       if (write_in_full(fd, blob, size) != size)
+       if (write_in_full(temp->tempfile->fd, blob, size) != size ||
+           close_tempfile_gently(temp->tempfile))
                die_errno("unable to write temp-file");
-       close_tempfile(&temp->tempfile);
-       temp->name = get_tempfile_path(&temp->tempfile);
+       temp->name = get_tempfile_path(temp->tempfile);
        oid_to_hex_r(temp->hex, oid);
        xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
        strbuf_release(&buf);
@@ -3242,7 +4008,7 @@ static void diff_fill_oid_info(struct diff_filespec *one)
                        }
                        if (lstat(one->path, &st) < 0)
                                die_errno("stat '%s'", one->path);
-                       if (index_path(one->oid.hash, one->path, &st, 0))
+                       if (index_path(&one->oid, one->path, &st, 0))
                                die("cannot hash %s", one->path);
                }
        }
@@ -3275,8 +4041,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
        const char *other;
        const char *attr_path;
 
-       name  = p->one->path;
-       other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+       name  = one->path;
+       other = (strcmp(name, two->path) ? two->path : NULL);
        attr_path = name;
        if (o->prefix_length)
                strip_prefix(o->prefix_length, &name, &other);
@@ -3399,6 +4165,8 @@ void diff_setup(struct diff_options *options)
                options->a_prefix = "a/";
                options->b_prefix = "b/";
        }
+
+       options->color_moved = diff_color_moved_default;
 }
 
 void diff_setup_done(struct diff_options *options)
@@ -3508,6 +4276,9 @@ void diff_setup_done(struct diff_options *options)
 
        if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1)
                die(_("--follow requires exactly one pathspec"));
+
+       if (!options->use_color || external_diff())
+               options->color_moved = 0;
 }
 
 static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
@@ -3932,7 +4703,19 @@ int diff_opt_parse(struct diff_options *options,
        }
        else if (!strcmp(arg, "--no-color"))
                options->use_color = 0;
-       else if (!strcmp(arg, "--color-words")) {
+       else if (!strcmp(arg, "--color-moved")) {
+               if (diff_color_moved_default)
+                       options->color_moved = diff_color_moved_default;
+               if (options->color_moved == COLOR_MOVED_NO)
+                       options->color_moved = COLOR_MOVED_DEFAULT;
+       } else if (!strcmp(arg, "--no-color-moved"))
+               options->color_moved = COLOR_MOVED_NO;
+       else if (skip_prefix(arg, "--color-moved=", &arg)) {
+               int cm = parse_color_moved(arg);
+               if (cm < 0)
+                       die("bad --color-moved argument: %s", arg);
+               options->color_moved = cm;
+       } else if (!strcmp(arg, "--color-words")) {
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
        }
@@ -4462,67 +5245,76 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
        }
 }
 
-static void show_file_mode_name(FILE *file, const char *newdelete, struct diff_filespec *fs)
+static void show_file_mode_name(struct diff_options *opt, const char *newdelete, struct diff_filespec *fs)
 {
+       struct strbuf sb = STRBUF_INIT;
        if (fs->mode)
-               fprintf(file, " %s mode %06o ", newdelete, fs->mode);
+               strbuf_addf(&sb, " %s mode %06o ", newdelete, fs->mode);
        else
-               fprintf(file, " %s ", newdelete);
-       write_name_quoted(fs->path, file, '\n');
-}
+               strbuf_addf(&sb, " %s ", newdelete);
 
+       quote_c_style(fs->path, &sb, NULL, 0);
+       strbuf_addch(&sb, '\n');
+       emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
+                        sb.buf, sb.len, 0);
+       strbuf_release(&sb);
+}
 
-static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name,
-               const char *line_prefix)
+static void show_mode_change(struct diff_options *opt, struct diff_filepair *p,
+               int show_name)
 {
        if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
-               fprintf(file, "%s mode change %06o => %06o%c", line_prefix, p->one->mode,
-                       p->two->mode, show_name ? ' ' : '\n');
+               struct strbuf sb = STRBUF_INIT;
+               strbuf_addf(&sb, " mode change %06o => %06o",
+                           p->one->mode, p->two->mode);
                if (show_name) {
-                       write_name_quoted(p->two->path, file, '\n');
+                       strbuf_addch(&sb, ' ');
+                       quote_c_style(p->two->path, &sb, NULL, 0);
                }
+               emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
+                                sb.buf, sb.len, 0);
+               strbuf_release(&sb);
        }
 }
 
-static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p,
-                       const char *line_prefix)
+static void show_rename_copy(struct diff_options *opt, const char *renamecopy,
+               struct diff_filepair *p)
 {
+       struct strbuf sb = STRBUF_INIT;
        char *names = pprint_rename(p->one->path, p->two->path);
-
-       fprintf(file, " %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
+       strbuf_addf(&sb, " %s %s (%d%%)\n",
+                       renamecopy, names, similarity_index(p));
        free(names);
-       show_mode_change(file, p, 0, line_prefix);
+       emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
+                                sb.buf, sb.len, 0);
+       show_mode_change(opt, p, 0);
 }
 
 static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
 {
-       FILE *file = opt->file;
-       const char *line_prefix = diff_line_prefix(opt);
-
        switch(p->status) {
        case DIFF_STATUS_DELETED:
-               fputs(line_prefix, file);
-               show_file_mode_name(file, "delete", p->one);
+               show_file_mode_name(opt, "delete", p->one);
                break;
        case DIFF_STATUS_ADDED:
-               fputs(line_prefix, file);
-               show_file_mode_name(file, "create", p->two);
+               show_file_mode_name(opt, "create", p->two);
                break;
        case DIFF_STATUS_COPIED:
-               fputs(line_prefix, file);
-               show_rename_copy(file, "copy", p, line_prefix);
+               show_rename_copy(opt, "copy", p);
                break;
        case DIFF_STATUS_RENAMED:
-               fputs(line_prefix, file);
-               show_rename_copy(file, "rename", p, line_prefix);
+               show_rename_copy(opt, "rename", p);
                break;
        default:
                if (p->score) {
-                       fprintf(file, "%s rewrite ", line_prefix);
-                       write_name_quoted(p->two->path, file, ' ');
-                       fprintf(file, "(%d%%)\n", similarity_index(p));
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addstr(&sb, " rewrite ");
+                       quote_c_style(p->two->path, &sb, NULL, 0);
+                       strbuf_addf(&sb, " (%d%%)\n", similarity_index(p));
+                       emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
+                                        sb.buf, sb.len, 0);
                }
-               show_mode_change(file, p, !p->score, line_prefix);
+               show_mode_change(opt, p, !p->score);
                break;
        }
 }
@@ -4727,6 +5519,51 @@ void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
                warning(_(rename_limit_advice), varname, needed);
 }
 
+static void diff_flush_patch_all_file_pairs(struct diff_options *o)
+{
+       int i;
+       static struct emitted_diff_symbols esm = EMITTED_DIFF_SYMBOLS_INIT;
+       struct diff_queue_struct *q = &diff_queued_diff;
+
+       if (WSEH_NEW & WS_RULE_MASK)
+               die("BUG: WS rules bit mask overlaps with diff symbol flags");
+
+       if (o->color_moved)
+               o->emitted_symbols = &esm;
+
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               if (check_pair_status(p))
+                       diff_flush_patch(p, o);
+       }
+
+       if (o->emitted_symbols) {
+               if (o->color_moved) {
+                       struct hashmap add_lines, del_lines;
+
+                       hashmap_init(&del_lines,
+                                    (hashmap_cmp_fn)moved_entry_cmp, o, 0);
+                       hashmap_init(&add_lines,
+                                    (hashmap_cmp_fn)moved_entry_cmp, o, 0);
+
+                       add_lines_to_move_detection(o, &add_lines, &del_lines);
+                       mark_color_as_moved(o, &add_lines, &del_lines);
+                       if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
+                               dim_moved_lines(o);
+
+                       hashmap_free(&add_lines, 0);
+                       hashmap_free(&del_lines, 0);
+               }
+
+               for (i = 0; i < esm.nr; i++)
+                       emit_diff_symbol_from_struct(o, &esm.buf[i]);
+
+               for (i = 0; i < esm.nr; i++)
+                       free((void *)esm.buf[i].line);
+       }
+       esm.nr = 0;
+}
+
 void diff_flush(struct diff_options *options)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
@@ -4799,6 +5636,7 @@ void diff_flush(struct diff_options *options)
                        fclose(options->file);
                options->file = xfopen("/dev/null", "w");
                options->close_file = 1;
+               options->color_moved = 0;
                for (i = 0; i < q->nr; i++) {
                        struct diff_filepair *p = q->queue[i];
                        if (check_pair_status(p))
@@ -4810,20 +5648,14 @@ void diff_flush(struct diff_options *options)
 
        if (output_format & DIFF_FORMAT_PATCH) {
                if (separator) {
-                       fprintf(options->file, "%s%c",
-                               diff_line_prefix(options),
-                               options->line_termination);
-                       if (options->stat_sep) {
+                       emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0);
+                       if (options->stat_sep)
                                /* attach patch instead of inline */
-                               fputs(options->stat_sep, options->file);
-                       }
+                               emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP,
+                                                NULL, 0, 0);
                }
 
-               for (i = 0; i < q->nr; i++) {
-                       struct diff_filepair *p = q->queue[i];
-                       if (check_pair_status(p))
-                               diff_flush_patch(p, options);
-               }
+               diff_flush_patch_all_file_pairs(options);
        }
 
        if (output_format & DIFF_FORMAT_CALLBACK)
diff --git a/diff.h b/diff.h
index 2d442e296f9821ea4085188602b9ea4a4ba159a6..aca150ba2e0f0b0a984e7f15c292af5f5b057d61 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -148,9 +148,9 @@ struct diff_options {
        int abbrev;
        int ita_invisible_in_index;
 /* white-space error highlighting */
-#define WSEH_NEW 1
-#define WSEH_CONTEXT 2
-#define WSEH_OLD 4
+#define WSEH_NEW (1<<12)
+#define WSEH_CONTEXT (1<<13)
+#define WSEH_OLD (1<<14)
        unsigned ws_error_highlight;
        const char *prefix;
        int prefix_length;
@@ -186,8 +186,27 @@ struct diff_options {
        void *output_prefix_data;
 
        int diff_path_counter;
+
+       struct emitted_diff_symbols *emitted_symbols;
+       enum {
+               COLOR_MOVED_NO = 0,
+               COLOR_MOVED_PLAIN = 1,
+               COLOR_MOVED_ZEBRA = 2,
+               COLOR_MOVED_ZEBRA_DIM = 3,
+       } color_moved;
+       #define COLOR_MOVED_DEFAULT COLOR_MOVED_ZEBRA
+       #define COLOR_MOVED_MIN_ALNUM_COUNT 20
 };
 
+void diff_emit_submodule_del(struct diff_options *o, const char *line);
+void diff_emit_submodule_add(struct diff_options *o, const char *line);
+void diff_emit_submodule_untracked(struct diff_options *o, const char *path);
+void diff_emit_submodule_modified(struct diff_options *o, const char *path);
+void diff_emit_submodule_header(struct diff_options *o, const char *header);
+void diff_emit_submodule_error(struct diff_options *o, const char *err);
+void diff_emit_submodule_pipethrough(struct diff_options *o,
+                                    const char *line, int len);
+
 enum color_diff {
        DIFF_RESET = 0,
        DIFF_CONTEXT = 1,
@@ -197,7 +216,15 @@ enum color_diff {
        DIFF_FILE_NEW = 5,
        DIFF_COMMIT = 6,
        DIFF_WHITESPACE = 7,
-       DIFF_FUNCINFO = 8
+       DIFF_FUNCINFO = 8,
+       DIFF_FILE_OLD_MOVED = 9,
+       DIFF_FILE_OLD_MOVED_ALT = 10,
+       DIFF_FILE_OLD_MOVED_DIM = 11,
+       DIFF_FILE_OLD_MOVED_ALT_DIM = 12,
+       DIFF_FILE_NEW_MOVED = 13,
+       DIFF_FILE_NEW_MOVED_ALT = 14,
+       DIFF_FILE_NEW_MOVED_DIM = 15,
+       DIFF_FILE_NEW_MOVED_ALT_DIM = 16
 };
 const char *diff_get_color(int diff_use_color, enum color_diff ix);
 #define diff_get_color_opt(o, ix) \
@@ -396,8 +423,8 @@ extern int parse_rename_score(const char **cp_p);
 
 extern long parse_algorithm_value(const char *value);
 
-extern int print_stat_summary(FILE *fp, int files,
-                             int insertions, int deletions);
+extern void print_stat_summary(FILE *fp, int files,
+                              int insertions, int deletions);
 extern void setup_diff_pager(struct diff_options *);
 
 #endif /* DIFF_H */
index 786f3894984b5652482f313aa489200dd7ff9355..0d8c3d2ee480d70718f1e6f0b43a1175a14e4584 100644 (file)
@@ -532,9 +532,9 @@ void diffcore_rename(struct diff_options *options)
        }
 
        if (options->show_rename_progress) {
-               progress = start_progress_delay(
+               progress = start_delayed_progress(
                                _("Performing inexact rename detection"),
-                               rename_dst_nr * rename_src_nr, 50, 1);
+                               rename_dst_nr * rename_src_nr);
        }
 
        mx = xcalloc(st_mult(NUM_CANDIDATE_PER_DST, num_create), sizeof(*mx));
diff --git a/entry.c b/entry.c
index 65458f07a4453fa29731fe2b9c20848d6b192b20..cb291aa88bf148608184edeff68943315b44e6ab 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -3,6 +3,7 @@
 #include "dir.h"
 #include "streaming.h"
 #include "submodule.h"
+#include "progress.h"
 
 static void create_directories(const char *path, int path_len,
                               const struct checkout *state)
@@ -161,16 +162,22 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
 int finish_delayed_checkout(struct checkout *state)
 {
        int errs = 0;
+       unsigned delayed_object_count;
+       off_t filtered_bytes = 0;
        struct string_list_item *filter, *path;
+       struct progress *progress;
        struct delayed_checkout *dco = state->delayed_checkout;
 
        if (!state->delayed_checkout)
                return errs;
 
        dco->state = CE_RETRY;
+       delayed_object_count = dco->paths.nr;
+       progress = start_delayed_progress(_("Filtering content"), delayed_object_count);
        while (dco->filters.nr > 0) {
                for_each_string_list_item(filter, &dco->filters) {
                        struct string_list available_paths = STRING_LIST_INIT_NODUP;
+                       display_progress(progress, delayed_object_count - dco->paths.nr);
 
                        if (!async_query_available_blobs(filter->string, &available_paths)) {
                                /* Filter reported an error */
@@ -216,11 +223,17 @@ int finish_delayed_checkout(struct checkout *state)
                                }
                                ce = index_file_exists(state->istate, path->string,
                                                       strlen(path->string), 0);
-                               errs |= (ce ? checkout_entry(ce, state, NULL) : 1);
+                               if (ce) {
+                                       errs |= checkout_entry(ce, state, NULL);
+                                       filtered_bytes += ce->ce_stat_data.sd_size;
+                                       display_throughput(progress, filtered_bytes);
+                               } else
+                                       errs = 1;
                        }
                }
                string_list_remove_empty_items(&dco->filters, 0);
        }
+       stop_progress(&progress);
        string_list_clear(&dco->filters, 0);
 
        /* At this point we should not have any delayed paths anymore. */
index 0f5837d48e7040e9d2b07513666b608353736645..2514bc22abf4aedfaa8d641976c96fbf222bb8a7 100644 (file)
@@ -165,7 +165,7 @@ to contribute an update.  This may be because you would want to improve
 the translation of existing messages, or because the git-gui software
 itself was updated and there are new messages that need translation.
 
-In any case, make sure you are up-to-date before starting your work:
+In any case, make sure you are up to date before starting your work:
 
        $ git checkout master
        $ git pull
index 6c390d6c229d7ef8cca8ae01d33b42b57e29c3b3..7d19d379512b52168d7c604002e872fc3eef9fd7 100755 (executable)
@@ -74,7 +74,7 @@ do
 
        case "$LF$common$LF" in
        *"$LF$SHA1$LF"*)
-               eval_gettextln "Already up-to-date with \$pretty_name"
+               eval_gettextln "Already up to date with \$pretty_name"
                continue
                ;;
        esac
index 2fa581789c5d9d4dd967e1783c513d64660f8a9c..76859b453afc2d5ba6897d15a680245b455f4f38 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -3614,7 +3614,7 @@ def run(self, args):
 
     def rebase(self):
         if os.system("git update-index --refresh") != 0:
-            die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash.");
+            die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up to date or stash away all your changes with git stash.");
         if len(read_pipe("git diff-index HEAD --")) > 0:
             die("You have uncommitted changes. Please commit them before rebasing or stash them away with git stash.");
 
index fa6526986ed40143a12ffcd6563fd52b49d209e7..2208dcc2139be7130749fd3bae462320f17d3b21 100755 (executable)
@@ -155,7 +155,6 @@ sub format_2822_time {
 }
 
 my $have_email_valid = eval { require Email::Valid; 1 };
-my $have_mail_address = eval { require Mail::Address; 1 };
 my $smtp;
 my $auth;
 my $num_sent = 0;
@@ -490,11 +489,7 @@ sub read_config {
 ($repocommitter) = Git::ident_person(@repo, 'committer');
 
 sub parse_address_line {
-       if ($have_mail_address) {
-               return map { $_->format } Mail::Address->parse($_[0]);
-       } else {
-               return Git::parse_mailboxes($_[0]);
-       }
+       return Git::parse_mailboxes($_[0]);
 }
 
 sub split_addrs {
@@ -1089,6 +1084,26 @@ sub sanitize_address {
 
 }
 
+sub strip_garbage_one_address {
+       my ($addr) = @_;
+       chomp $addr;
+       if ($addr =~ /^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {
+               # "Foo Bar" <foobar@example.com> [possibly garbage here]
+               # Foo Bar <foobar@example.com> [possibly garbage here]
+               return $1;
+       }
+       if ($addr =~ /^(<[^>]*>).*/) {
+               # <foo@example.com> [possibly garbage here]
+               # if garbage contains other addresses, they are ignored.
+               return $1;
+       }
+       if ($addr =~ /^([^"#,\s]*)/) {
+               # address without quoting: remove anything after the address
+               return $1;
+       }
+       return $addr;
+}
+
 sub sanitize_address_list {
        return (map { sanitize_address($_) } @_);
 }
@@ -1590,10 +1605,12 @@ sub send_message {
        # Now parse the message body
        while(<$fh>) {
                $message .=  $_;
-               if (/^(Signed-off-by|Cc): ([^>]*>?)/i) {
+               if (/^(Signed-off-by|Cc): (.*)/i) {
                        chomp;
                        my ($what, $c) = ($1, $2);
-                       chomp $c;
+                       # strip garbage for the address we'll use:
+                       $c = strip_garbage_one_address($c);
+                       # sanitize a bit more to decide whether to suppress the address:
                        my $sc = sanitize_address($c);
                        if ($sc eq $sender) {
                                next if ($suppress_cc{'self'});
index 9208f42ed1753530b8ea46761f8ecc61bbe95976..959f04b494e610258488867aa7de013a3477e5fc 100755 (executable)
@@ -5967,6 +5967,9 @@ sub git_history_body {
                      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
 
                if ($ftype eq 'blob') {
+                       print " | " .
+                             $cgi->a({-href => href(action=>"blob_plain", hash_base=>$commit, file_name=>$file_name)}, "raw");
+
                        my $blob_current = $file_hash;
                        my $blob_parent  = git_get_hash_by_path($commit, $file_name);
                        if (defined $blob_current && defined $blob_parent &&
index d936f3a32fe5d38c2b41ae0488ac2c02250a1618..4feacf16e5bcd93dcde4a2ea769f6b61ca369593 100644 (file)
@@ -202,26 +202,26 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
                         struct strbuf *gpg_output, struct strbuf *gpg_status)
 {
        struct child_process gpg = CHILD_PROCESS_INIT;
-       static struct tempfile temp;
-       int fd, ret;
+       struct tempfile *temp;
+       int ret;
        struct strbuf buf = STRBUF_INIT;
 
-       fd = mks_tempfile_t(&temp, ".git_vtag_tmpXXXXXX");
-       if (fd < 0)
+       temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
+       if (!temp)
                return error_errno(_("could not create temporary file"));
-       if (write_in_full(fd, signature, signature_size) < 0) {
+       if (write_in_full(temp->fd, signature, signature_size) < 0 ||
+           close_tempfile_gently(temp) < 0) {
                error_errno(_("failed writing detached signature to '%s'"),
-                           temp.filename.buf);
+                           temp->filename.buf);
                delete_tempfile(&temp);
                return -1;
        }
-       close(fd);
 
        argv_array_pushl(&gpg.args,
                         gpg_program,
                         "--status-fd=1",
                         "--keyid-format=long",
-                        "--verify", temp.filename.buf, "-",
+                        "--verify", temp->filename.buf, "-",
                         NULL);
 
        if (!gpg_status)
diff --git a/hash.h b/hash.h
index bef3e630a093b508e78419c5f4e228e836ecb88c..024d0d3d50b174aac1d948e9ecfa7ae92020355a 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -8,11 +8,7 @@
 #elif defined(SHA1_OPENSSL)
 #include <openssl/sha.h>
 #elif defined(SHA1_DC)
-#ifdef DC_SHA1_SUBMODULE
-#include "sha1collisiondetection/lib/sha1.h"
-#else
-#include "sha1dc/sha1.h"
-#endif
+#include "sha1dc_git.h"
 #else /* SHA1_BLK */
 #include "block-sha1/sha1.h"
 #endif
diff --git a/http.c b/http.c
index 59bf8833af95a25a1968eaa676b1feed6c694813..9e40a465fdec6467726e980da413478b9cf78492 100644 (file)
--- a/http.c
+++ b/http.c
@@ -92,7 +92,7 @@ static struct {
         * here, too
         */
 };
-#if LIBCURL_VERSION_NUM >= 0x071600
+#ifdef CURLGSSAPI_DELEGATION_FLAG
 static const char *curl_deleg;
 static struct {
        const char *name;
@@ -353,7 +353,7 @@ static int http_options(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp("http.delegation", var)) {
-#if LIBCURL_VERSION_NUM >= 0x071600
+#ifdef CURLGSSAPI_DELEGATION_FLAG
                return git_config_string(&curl_deleg, var, value);
 #else
                warning(_("Delegation control is not supported with cURL < 7.22.0"));
@@ -678,6 +678,7 @@ void setup_curl_trace(CURL *handle)
        curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL);
 }
 
+#ifdef CURLPROTO_HTTP
 static long get_curl_allowed_protocols(int from_user)
 {
        long allowed_protocols = 0;
@@ -693,6 +694,7 @@ static long get_curl_allowed_protocols(int from_user)
 
        return allowed_protocols;
 }
+#endif
 
 static CURL *get_curl_handle(void)
 {
@@ -718,7 +720,7 @@ static CURL *get_curl_handle(void)
        curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
 #endif
 
-#if LIBCURL_VERSION_NUM >= 0x071600
+#ifdef CURLGSSAPI_DELEGATION_FLAG
        if (curl_deleg) {
                int i;
                for (i = 0; i < ARRAY_SIZE(curl_deleg_levels); i++) {
@@ -791,7 +793,7 @@ static CURL *get_curl_handle(void)
 #elif LIBCURL_VERSION_NUM >= 0x071101
        curl_easy_setopt(result, CURLOPT_POST301, 1);
 #endif
-#if LIBCURL_VERSION_NUM >= 0x071304
+#ifdef CURLPROTO_HTTP
        curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS,
                         get_curl_allowed_protocols(0));
        curl_easy_setopt(result, CURLOPT_PROTOCOLS,
diff --git a/list.h b/list.h
index a226a870dc09d6b58346a92d5f98078d2e55163a..eb601192f4ca9a6af126c82f4c2a24cb9145009d 100644 (file)
--- a/list.h
+++ b/list.h
@@ -163,4 +163,42 @@ static inline void list_replace_init(struct list_head *old,
        INIT_LIST_HEAD(old);
 }
 
+/*
+ * This is exactly the same as a normal list_head, except that it can be
+ * declared volatile (e.g., if you have a list that may be accessed from signal
+ * handlers).
+ */
+struct volatile_list_head {
+       volatile struct volatile_list_head *next, *prev;
+};
+
+#define VOLATILE_LIST_HEAD(name) \
+       volatile struct volatile_list_head name = { &(name), &(name) }
+
+static inline void __volatile_list_del(volatile struct volatile_list_head *prev,
+                                      volatile struct volatile_list_head *next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+static inline void volatile_list_del(volatile struct volatile_list_head *elem)
+{
+       __volatile_list_del(elem->prev, elem->next);
+}
+
+static inline int volatile_list_empty(volatile struct volatile_list_head *head)
+{
+       return head == head->next;
+}
+
+static inline void volatile_list_add(volatile struct volatile_list_head *newp,
+                                    volatile struct volatile_list_head *head)
+{
+       head->next->prev = newp;
+       newp->next = head->next;
+       newp->prev = head;
+       head->next = newp;
+}
+
 #endif /* LIST_H */
index aa69210d8b3a9063517d2d84153dc3ba738bef30..efcb7d7dfe30a2fbad03244ba4ca9e536a3649e4 100644 (file)
@@ -72,7 +72,6 @@ static void resolve_symlink(struct strbuf *path)
 /* Make sure errno contains a meaningful value on error */
 static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-       int fd;
        struct strbuf filename = STRBUF_INIT;
 
        strbuf_addstr(&filename, path);
@@ -80,9 +79,9 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
                resolve_symlink(&filename);
 
        strbuf_addstr(&filename, LOCK_SUFFIX);
-       fd = create_tempfile(&lk->tempfile, filename.buf);
+       lk->tempfile = create_tempfile(filename.buf);
        strbuf_release(&filename);
-       return fd;
+       return lk->tempfile ? lk->tempfile->fd : -1;
 }
 
 /*
@@ -191,7 +190,7 @@ char *get_locked_file_path(struct lock_file *lk)
 {
        struct strbuf ret = STRBUF_INIT;
 
-       strbuf_addstr(&ret, get_tempfile_path(&lk->tempfile));
+       strbuf_addstr(&ret, get_tempfile_path(lk->tempfile));
        if (ret.len <= LOCK_SUFFIX_LEN ||
            strcmp(ret.buf + ret.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
                die("BUG: get_locked_file_path() called for malformed lock object");
index 572064939c718281b56abda7757bb21d35f42de3..7c1c484d7cfdfab126fd0ce947ac40f650e5d107 100644 (file)
  *
  * The caller:
  *
- * * Allocates a `struct lock_file` either as a static variable or on
- *   the heap, initialized to zeros. Once you use the structure to
- *   call the `hold_lock_file_for_*()` family of functions, it belongs
- *   to the lockfile subsystem and its storage must remain valid
- *   throughout the life of the program (i.e. you cannot use an
- *   on-stack variable to hold this structure).
+ * * Allocates a `struct lock_file` with whatever storage duration you
+ *   desire. The struct does not have to be initialized before being
+ *   used, but it is good practice to do so using by setting it to
+ *   all-zeros (or using the LOCK_INIT macro). This puts the object in a
+ *   consistent state that allows you to call rollback_lock_file() even
+ *   if the lock was never taken (in which case it is a noop).
  *
  * * Attempts to create a lockfile by calling `hold_lock_file_for_update()`.
  *
  *   `rollback_lock_file()`.
  *
  * * Close the file descriptor without removing or renaming the
- *   lockfile by calling `close_lock_file()`, and later call
+ *   lockfile by calling `close_lock_file_gently()`, and later call
  *   `commit_lock_file()`, `commit_lock_file_to()`,
  *   `rollback_lock_file()`, or `reopen_lock_file()`.
  *
- * Even after the lockfile is committed or rolled back, the
- * `lock_file` object must not be freed or altered by the caller.
- * However, it may be reused; just pass it to another call of
- * `hold_lock_file_for_update()`.
+ * After the lockfile is committed or rolled back, the `lock_file`
+ * object can be discarded or reused.
  *
  * If the program exits before `commit_lock_file()`,
  * `commit_lock_file_to()`, or `rollback_lock_file()` is called, the
@@ -85,7 +83,7 @@
  *
  * If you need to close the file descriptor you obtained from a
  * `hold_lock_file_for_*()` function yourself, do so by calling
- * `close_lock_file()`. See "tempfile.h" for more information.
+ * `close_lock_file_gently()`. See "tempfile.h" for more information.
  *
  *
  * Under the covers, a lockfile is just a tempfile with a few helper
  *
  * Similarly, `commit_lock_file`, `commit_lock_file_to`, and
  * `close_lock_file` return 0 on success. On failure they set `errno`
- * appropriately, do their best to roll back the lockfile, and return
- * -1.
+ * appropriately and return -1. The `commit` variants (but not `close`)
+ * do their best to delete the temporary file before returning.
  */
 
 #include "tempfile.h"
 
 struct lock_file {
-       struct tempfile tempfile;
+       struct tempfile *tempfile;
 };
 
+#define LOCK_INIT { NULL }
+
 /* String appended to a filename to derive the lockfile name: */
 #define LOCK_SUFFIX ".lock"
 #define LOCK_SUFFIX_LEN 5
@@ -180,7 +180,7 @@ static inline int hold_lock_file_for_update(
  */
 static inline int is_lock_file_locked(struct lock_file *lk)
 {
-       return is_tempfile_active(&lk->tempfile);
+       return is_tempfile_active(lk->tempfile);
 }
 
 /*
@@ -202,12 +202,13 @@ extern NORETURN void unable_to_lock_die(const char *path, int err);
 /*
  * Associate a stdio stream with the lockfile (which must still be
  * open). Return `NULL` (*without* rolling back the lockfile) on
- * error. The stream is closed automatically when `close_lock_file()`
- * is called or when the file is committed or rolled back.
+ * error. The stream is closed automatically when
+ * `close_lock_file_gently()` is called or when the file is committed or
+ * rolled back.
  */
 static inline FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
 {
-       return fdopen_tempfile(&lk->tempfile, mode);
+       return fdopen_tempfile(lk->tempfile, mode);
 }
 
 /*
@@ -216,17 +217,17 @@ static inline FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
  */
 static inline const char *get_lock_file_path(struct lock_file *lk)
 {
-       return get_tempfile_path(&lk->tempfile);
+       return get_tempfile_path(lk->tempfile);
 }
 
 static inline int get_lock_file_fd(struct lock_file *lk)
 {
-       return get_tempfile_fd(&lk->tempfile);
+       return get_tempfile_fd(lk->tempfile);
 }
 
 static inline FILE *get_lock_file_fp(struct lock_file *lk)
 {
-       return get_tempfile_fp(&lk->tempfile);
+       return get_tempfile_fp(lk->tempfile);
 }
 
 /*
@@ -241,22 +242,21 @@ extern char *get_locked_file_path(struct lock_file *lk);
  * lockfile over the file being locked. Return 0 upon success. On
  * failure to `close(2)`, return a negative value and roll back the
  * lock file. Usually `commit_lock_file()`, `commit_lock_file_to()`,
- * or `rollback_lock_file()` should eventually be called if
- * `close_lock_file()` succeeds.
+ * or `rollback_lock_file()` should eventually be called.
  */
-static inline int close_lock_file(struct lock_file *lk)
+static inline int close_lock_file_gently(struct lock_file *lk)
 {
-       return close_tempfile(&lk->tempfile);
+       return close_tempfile_gently(lk->tempfile);
 }
 
 /*
- * Re-open a lockfile that has been closed using `close_lock_file()`
+ * Re-open a lockfile that has been closed using `close_lock_file_gently()`
  * but not yet committed or rolled back. This can be used to implement
  * a sequence of operations like the following:
  *
  * * Lock file.
  *
- * * Write new contents to lockfile, then `close_lock_file()` to
+ * * Write new contents to lockfile, then `close_lock_file_gently()` to
  *   cause the contents to be written to disk.
  *
  * * Pass the name of the lockfile to another program to allow it (and
@@ -270,7 +270,7 @@ static inline int close_lock_file(struct lock_file *lk)
  */
 static inline int reopen_lock_file(struct lock_file *lk)
 {
-       return reopen_tempfile(&lk->tempfile);
+       return reopen_tempfile(lk->tempfile);
 }
 
 /*
index 1494ffdb828106882053065e3ac8cc383136cf80..182626c02781384733b863b025bcfc5a1978e893 100644 (file)
@@ -1927,7 +1927,7 @@ int merge_trees(struct merge_options *o,
        }
 
        if (oid_eq(&common->object.oid, &merge->object.oid)) {
-               output(o, 0, _("Already up-to-date!"));
+               output(o, 0, _("Already up to date!"));
                *result = head;
                return 1;
        }
index c12b354f1003a29c6faf58e7ec970e4ac761812e..b04d2f213116a6413d564107a6e12e3f7317accb 100644 (file)
@@ -624,7 +624,7 @@ int notes_merge(struct notes_merge_options *o,
        if (!oidcmp(&remote->object.oid, base_oid)) {
                /* Already merged; result == local commit */
                if (o->verbosity >= 2)
-                       printf("Already up-to-date!\n");
+                       printf("Already up to date!\n");
                oidcpy(result_oid, &local->object.oid);
                goto found_result;
        }
@@ -709,7 +709,7 @@ int notes_merge_commit(struct notes_merge_options *o,
                /* write file as blob, and add to partial_tree */
                if (stat(path.buf, &st))
                        die_errno("Failed to stat '%s'", path.buf);
-               if (index_path(blob_oid.hash, path.buf, &st, HASH_WRITE_OBJECT))
+               if (index_path(&blob_oid, path.buf, &st, HASH_WRITE_OBJECT))
                        die("Failed to write blob object from '%s'", path.buf);
                if (add_note(partial_tree, &obj_oid, &blob_oid, NULL))
                        die("Failed to add resolved note '%s' to notes tree",
diff --git a/notes.c b/notes.c
index 503754d79ebdca94fb102ccff7886ccef30f31e5..5c62862574399acb486530d3fab5a7f659eb7c2f 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -64,7 +64,7 @@ struct non_note {
 #define CLR_PTR_TYPE(ptr)       ((void *) ((uintptr_t) (ptr) & ~3))
 #define SET_PTR_TYPE(ptr, type) ((void *) ((uintptr_t) (ptr) | (type)))
 
-#define GET_NIBBLE(n, sha1) (((sha1[(n) >> 1]) >> ((~(n) & 0x01) << 2)) & 0x0f)
+#define GET_NIBBLE(n, sha1) ((((sha1)[(n) >> 1]) >> ((~(n) & 0x01) << 2)) & 0x0f)
 
 #define KEY_INDEX (GIT_SHA1_RAWSZ - 1)
 #define FANOUT_PATH_SEPARATORS ((GIT_SHA1_HEXSZ / 2) - 1)
@@ -335,31 +335,20 @@ static void note_tree_free(struct int_node *tree)
 }
 
 /*
- * Convert a partial SHA1 hex string to the corresponding partial SHA1 value.
- * - hex      - Partial SHA1 segment in ASCII hex format
- * - hex_len  - Length of above segment. Must be multiple of 2 between 0 and 40
- * - sha1     - Partial SHA1 value is written here
- * - sha1_len - Max #bytes to store in sha1, Must be >= hex_len / 2, and < 20
- * Returns -1 on error (invalid arguments or invalid SHA1 (not in hex format)).
- * Otherwise, returns number of bytes written to sha1 (i.e. hex_len / 2).
- * Pads sha1 with NULs up to sha1_len (not included in returned length).
+ * 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).
  */
-static int get_oid_hex_segment(const char *hex, unsigned int hex_len,
-               unsigned char *oid, unsigned int oid_len)
+static int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
 {
-       unsigned int i, len = hex_len >> 1;
-       if (hex_len % 2 != 0 || len > oid_len)
-               return -1;
-       for (i = 0; i < len; i++) {
+       for (; len; len--, hex += 2) {
                unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
+
                if (val & ~0xff)
                        return -1;
-               *oid++ = val;
-               hex += 2;
+               *binary++ = val;
        }
-       for (; i < oid_len; i++)
-               *oid++ = 0;
-       return len;
+       return 0;
 }
 
 static int non_note_cmp(const struct non_note *a, const struct non_note *b)
@@ -417,80 +406,90 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
                struct int_node *node, unsigned int n)
 {
        struct object_id object_oid;
-       unsigned int prefix_len;
+       size_t prefix_len;
        void *buf;
        struct tree_desc desc;
        struct name_entry entry;
-       int len, path_len;
-       unsigned char type;
-       struct leaf_node *l;
 
-       buf = fill_tree_descriptor(&desc, subtree->val_oid.hash);
+       buf = fill_tree_descriptor(&desc, &subtree->val_oid);
        if (!buf)
                die("Could not read %s for notes-index",
                     oid_to_hex(&subtree->val_oid));
 
        prefix_len = subtree->key_oid.hash[KEY_INDEX];
-       assert(prefix_len * 2 >= n);
+       if (prefix_len >= GIT_SHA1_RAWSZ)
+               BUG("prefix_len (%"PRIuMAX") is out of range", (uintmax_t)prefix_len);
+       if (prefix_len * 2 < n)
+               BUG("prefix_len (%"PRIuMAX") is too small", (uintmax_t)prefix_len);
        memcpy(object_oid.hash, subtree->key_oid.hash, prefix_len);
        while (tree_entry(&desc, &entry)) {
-               path_len = strlen(entry.path);
-               len = get_oid_hex_segment(entry.path, path_len,
-                               object_oid.hash + prefix_len, GIT_SHA1_RAWSZ - prefix_len);
-               if (len < 0)
-                       goto handle_non_note; /* entry.path is not a SHA1 */
-               len += prefix_len;
+               unsigned char type;
+               struct leaf_node *l;
+               size_t path_len = strlen(entry.path);
+
+               if (path_len == 2 * (GIT_SHA1_RAWSZ - prefix_len)) {
+                       /* This is potentially the remainder of the SHA-1 */
+
+                       if (!S_ISREG(entry.mode))
+                               /* notes must be blobs */
+                               goto handle_non_note;
+
+                       if (hex_to_bytes(object_oid.hash + prefix_len, entry.path,
+                                        GIT_SHA1_RAWSZ - prefix_len))
+                               goto handle_non_note; /* entry.path is not a SHA1 */
 
-               /*
-                * If object SHA1 is complete (len == 20), assume note object
-                * If object SHA1 is incomplete (len < 20), and current
-                * component consists of 2 hex chars, assume note subtree
-                */
-               if (len <= GIT_SHA1_RAWSZ) {
                        type = PTR_TYPE_NOTE;
-                       l = (struct leaf_node *)
-                               xcalloc(1, sizeof(struct leaf_node));
-                       oidcpy(&l->key_oid, &object_oid);
-                       oidcpy(&l->val_oid, entry.oid);
-                       if (len < GIT_SHA1_RAWSZ) {
-                               if (!S_ISDIR(entry.mode) || path_len != 2)
-                                       goto handle_non_note; /* not subtree */
-                               l->key_oid.hash[KEY_INDEX] = (unsigned char) len;
-                               type = PTR_TYPE_SUBTREE;
-                       }
-                       if (note_tree_insert(t, node, n, l, type,
-                                            combine_notes_concatenate))
-                               die("Failed to load %s %s into notes tree "
-                                   "from %s",
-                                   type == PTR_TYPE_NOTE ? "note" : "subtree",
-                                   oid_to_hex(&l->key_oid), t->ref);
+               } else if (path_len == 2) {
+                       /* This is potentially an internal node */
+                       size_t len = prefix_len;
+
+                       if (!S_ISDIR(entry.mode))
+                               /* internal nodes must be trees */
+                               goto handle_non_note;
+
+                       if (hex_to_bytes(object_oid.hash + len++, entry.path, 1))
+                               goto handle_non_note; /* entry.path is not a SHA1 */
+
+                       /*
+                        * Pad the rest of the SHA-1 with zeros,
+                        * except for the last byte, where we write
+                        * the length:
+                        */
+                       memset(object_oid.hash + len, 0, GIT_SHA1_RAWSZ - len - 1);
+                       object_oid.hash[KEY_INDEX] = (unsigned char)len;
+
+                       type = PTR_TYPE_SUBTREE;
+               } else {
+                       /* This can't be part of a note */
+                       goto handle_non_note;
                }
+
+               l = xcalloc(1, sizeof(*l));
+               oidcpy(&l->key_oid, &object_oid);
+               oidcpy(&l->val_oid, entry.oid);
+               if (note_tree_insert(t, node, n, l, type,
+                                    combine_notes_concatenate))
+                       die("Failed to load %s %s into notes tree "
+                           "from %s",
+                           type == PTR_TYPE_NOTE ? "note" : "subtree",
+                           oid_to_hex(&l->key_oid), t->ref);
+
                continue;
 
 handle_non_note:
                /*
-                * Determine full path for this non-note entry:
-                * The filename is already found in entry.path, but the
-                * directory part of the path must be deduced from the subtree
-                * containing this entry. We assume here that the overall notes
-                * tree follows a strict byte-based progressive fanout
-                * structure (i.e. using 2/38, 2/2/36, etc. fanouts, and not
-                * e.g. 4/36 fanout). This means that if a non-note is found at
-                * path "dead/beef", the following code will register it as
-                * being found on "de/ad/beef".
-                * On the other hand, if you use such non-obvious non-note
-                * paths in the middle of a notes tree, you deserve what's
-                * coming to you ;). Note that for non-notes that are not
-                * SHA1-like at the top level, there will be no problems.
-                *
-                * To conclude, it is strongly advised to make sure non-notes
-                * have at least one non-hex character in the top-level path
-                * component.
+                * Determine full path for this non-note entry. The
+                * filename is already found in entry.path, but the
+                * directory part of the path must be deduced from the
+                * subtree containing this entry based on our
+                * knowledge that the overall notes tree follows a
+                * strict byte-based progressive fanout structure
+                * (i.e. using 2/38, 2/2/36, etc. fanouts).
                 */
                {
                        struct strbuf non_note_path = STRBUF_INIT;
                        const char *q = oid_to_hex(&subtree->key_oid);
-                       int i;
+                       size_t i;
                        for (i = 0; i < prefix_len; i++) {
                                strbuf_addch(&non_note_path, *q++);
                                strbuf_addch(&non_note_path, *q++);
index 7db9119573abe2c8308bd37c670c027c07b425c8..f364944b931a756b3b4819fb49ff5e9e01daf676 100644 (file)
@@ -136,9 +136,10 @@ static void format_packet(struct strbuf *out, const char *fmt, va_list args)
 static int packet_write_fmt_1(int fd, int gently,
                              const char *fmt, va_list args)
 {
-       struct strbuf buf = STRBUF_INIT;
+       static struct strbuf buf = STRBUF_INIT;
        ssize_t count;
 
+       strbuf_reset(&buf);
        format_packet(&buf, fmt, args);
        count = write_in_full(fd, buf.buf, buf.len);
        if (count == buf.len)
index 39cad5112b603df9c8ed927e738b2f7917da7522..94eab5c89ee1a1fa696b9fc491a5846008a2a255 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -871,16 +871,6 @@ const char *format_subject(struct strbuf *sb, const char *msg,
        return msg;
 }
 
-static void format_trailers(struct strbuf *sb, const char *msg)
-{
-       struct trailer_info info;
-
-       trailer_info_get(&info, msg);
-       strbuf_add(sb, info.trailer_start,
-                  info.trailer_end - info.trailer_start);
-       trailer_info_release(&info);
-}
-
 static void parse_commit_message(struct format_commit_context *c)
 {
        const char *msg = c->message + c->message_off;
@@ -1074,6 +1064,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
        const struct commit *commit = c->commit;
        const char *msg = c->message;
        struct commit_list *p;
+       const char *arg;
        int ch;
 
        /* these are independent of the commit */
@@ -1292,9 +1283,18 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                return 1;
        }
 
-       if (starts_with(placeholder, "(trailers)")) {
-               format_trailers(sb, msg + c->subject_off);
-               return strlen("(trailers)");
+       if (skip_prefix(placeholder, "(trailers", &arg)) {
+               struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+               while (*arg == ':') {
+                       if (skip_prefix(arg, ":only", &arg))
+                               opts.only_trailers = 1;
+                       else if (skip_prefix(arg, ":unfold", &arg))
+                               opts.unfold = 1;
+               }
+               if (*arg == ')') {
+                       format_trailers_from_commit(sb, msg + c->subject_off, &opts);
+                       return arg - placeholder + 1;
+               }
        }
 
        return 0;       /* unknown placeholder */
index 73e36d4a423067756ccd52af8cdb4cf0f762684b..289678d43d801411dbe56b8090a2e1a18a158499 100644 (file)
@@ -34,7 +34,7 @@ struct progress {
        unsigned total;
        unsigned last_percent;
        unsigned delay;
-       unsigned delayed_percent_treshold;
+       unsigned delayed_percent_threshold;
        struct throughput *throughput;
        uint64_t start_ns;
 };
@@ -88,7 +88,7 @@ static int display(struct progress *progress, unsigned n, const char *done)
                        return 0;
                if (progress->total) {
                        unsigned percent = n * 100 / progress->total;
-                       if (percent > progress->delayed_percent_treshold) {
+                       if (percent > progress->delayed_percent_threshold) {
                                /* inhibit this progress report entirely */
                                clear_progress_signal();
                                progress->delay = -1;
@@ -205,8 +205,8 @@ int display_progress(struct progress *progress, unsigned n)
        return progress ? display(progress, n, NULL) : 0;
 }
 
-struct progress *start_progress_delay(const char *title, unsigned total,
-                                      unsigned percent_treshold, unsigned delay)
+static struct progress *start_progress_delay(const char *title, unsigned total,
+                                            unsigned percent_threshold, unsigned delay)
 {
        struct progress *progress = malloc(sizeof(*progress));
        if (!progress) {
@@ -219,7 +219,7 @@ struct progress *start_progress_delay(const char *title, unsigned total,
        progress->total = total;
        progress->last_value = -1;
        progress->last_percent = -1;
-       progress->delayed_percent_treshold = percent_treshold;
+       progress->delayed_percent_threshold = percent_threshold;
        progress->delay = delay;
        progress->throughput = NULL;
        progress->start_ns = getnanotime();
@@ -227,6 +227,11 @@ struct progress *start_progress_delay(const char *title, unsigned total,
        return progress;
 }
 
+struct progress *start_delayed_progress(const char *title, unsigned total)
+{
+       return start_progress_delay(title, total, 0, 2);
+}
+
 struct progress *start_progress(const char *title, unsigned total)
 {
        return start_progress_delay(title, total, 0, 0);
index 611e4c4d42d8d1164add09f926ad5b2ce088db5e..6392b633710c84dcff3a238a26f4d4b359053059 100644 (file)
@@ -6,8 +6,7 @@ struct progress;
 void display_throughput(struct progress *progress, off_t total);
 int display_progress(struct progress *progress, unsigned n);
 struct progress *start_progress(const char *title, unsigned total);
-struct progress *start_progress_delay(const char *title, unsigned total,
-                                      unsigned percent_treshold, unsigned delay);
+struct progress *start_delayed_progress(const char *title, unsigned total);
 void stop_progress(struct progress **progress);
 void stop_progress_msg(struct progress **progress, const char *msg);
 
index d1ac5d97efdaff5dc971e38da278dcf1d820ef2c..88d7d679da9c837463f74d9a9277025b008635af 100644 (file)
@@ -10,6 +10,7 @@
 #include "progress.h"
 #include "list-objects.h"
 #include "packfile.h"
+#include "worktree.h"
 
 struct connectivity_progress {
        struct progress *progress;
@@ -177,6 +178,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
 
        /* detached HEAD is not included in the list above */
        head_ref(add_one_ref, revs);
+       other_head_refs(add_one_ref, revs);
 
        /* Add all reflog info */
        if (mark_reflog)
index acfb028f480b5545aa82d5c0533fe8bdfc581dfd..b211c57af6b418a24bb2eef97dabed0c95e53599 100644 (file)
@@ -160,9 +160,9 @@ static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
        int fd = git_open_cloexec(ce->name, O_RDONLY);
 
        if (fd >= 0) {
-               unsigned char sha1[20];
-               if (!index_fd(sha1, fd, st, OBJ_BLOB, ce->name, 0))
-                       match = hashcmp(sha1, ce->oid.hash);
+               struct object_id oid;
+               if (!index_fd(&oid, fd, st, OBJ_BLOB, ce->name, 0))
+                       match = oidcmp(&oid, &ce->oid);
                /* index_fd() closed the file descriptor already */
        }
        return match;
@@ -689,7 +689,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                return 0;
        }
        if (!intent_only) {
-               if (index_path(ce->oid.hash, path, st, HASH_WRITE_OBJECT)) {
+               if (index_path(&ce->oid, path, st, HASH_WRITE_OBJECT)) {
                        free(ce);
                        return error("unable to index file %s", path);
                }
@@ -1499,6 +1499,7 @@ struct ondisk_cache_entry_extended {
 };
 
 /* These are only used for v3 or lower */
+#define align_padding_size(size, len) ((size + (len) + 8) & ~7) - (size + len)
 #define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
 #define ondisk_cache_entry_size(len) align_flex_name(ondisk_cache_entry,len)
 #define ondisk_cache_entry_extended_size(len) align_flex_name(ondisk_cache_entry_extended,len)
@@ -2032,7 +2033,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
 }
 
 /* Copy miscellaneous fields but not the name */
-static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
+static void copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
                                       struct cache_entry *ce)
 {
        short flags;
@@ -2056,32 +2057,35 @@ static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
                struct ondisk_cache_entry_extended *ondisk2;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
                ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
-               return ondisk2->name;
-       }
-       else {
-               return ondisk->name;
        }
 }
 
 static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
-                         struct strbuf *previous_name)
+                         struct strbuf *previous_name, struct ondisk_cache_entry *ondisk)
 {
        int size;
-       struct ondisk_cache_entry *ondisk;
        int saved_namelen = saved_namelen; /* compiler workaround */
-       char *name;
        int result;
+       static unsigned char padding[8] = { 0x00 };
 
        if (ce->ce_flags & CE_STRIP_NAME) {
                saved_namelen = ce_namelen(ce);
                ce->ce_namelen = 0;
        }
 
+       if (ce->ce_flags & CE_EXTENDED)
+               size = offsetof(struct ondisk_cache_entry_extended, name);
+       else
+               size = offsetof(struct ondisk_cache_entry, name);
+
        if (!previous_name) {
-               size = ondisk_ce_size(ce);
-               ondisk = xcalloc(1, size);
-               name = copy_cache_entry_to_ondisk(ondisk, ce);
-               memcpy(name, ce->name, ce_namelen(ce));
+               int len = ce_namelen(ce);
+               copy_cache_entry_to_ondisk(ondisk, ce);
+               result = ce_write(c, fd, ondisk, size);
+               if (!result)
+                       result = ce_write(c, fd, ce->name, len);
+               if (!result)
+                       result = ce_write(c, fd, padding, align_padding_size(size, len));
        } else {
                int common, to_remove, prefix_size;
                unsigned char to_remove_vi[16];
@@ -2094,16 +2098,12 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
                to_remove = previous_name->len - common;
                prefix_size = encode_varint(to_remove, to_remove_vi);
 
-               if (ce->ce_flags & CE_EXTENDED)
-                       size = offsetof(struct ondisk_cache_entry_extended, name);
-               else
-                       size = offsetof(struct ondisk_cache_entry, name);
-               size += prefix_size + (ce_namelen(ce) - common + 1);
-
-               ondisk = xcalloc(1, size);
-               name = copy_cache_entry_to_ondisk(ondisk, ce);
-               memcpy(name, to_remove_vi, prefix_size);
-               memcpy(name + prefix_size, ce->name + common, ce_namelen(ce) - common);
+               copy_cache_entry_to_ondisk(ondisk, ce);
+               result = ce_write(c, fd, ondisk, size);
+               if (!result)
+                       result = ce_write(c, fd, to_remove_vi, prefix_size);
+               if (!result)
+                       result = ce_write(c, fd, ce->name + common, ce_namelen(ce) - common + 1);
 
                strbuf_splice(previous_name, common, to_remove,
                              ce->name + common, ce_namelen(ce) - common);
@@ -2113,8 +2113,6 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
                ce->ce_flags &= ~CE_STRIP_NAME;
        }
 
-       result = ce_write(c, fd, ondisk, size);
-       free(ondisk);
        return result;
 }
 
@@ -2192,10 +2190,11 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        int newfd = tempfile->fd;
        git_SHA_CTX c;
        struct cache_header hdr;
-       int i, err, removed, extended, hdr_version;
+       int i, err = 0, removed, extended, hdr_version;
        struct cache_entry **cache = istate->cache;
        int entries = istate->cache_nr;
        struct stat st;
+       struct ondisk_cache_entry_extended ondisk;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
        int drop_cache_tree = 0;
 
@@ -2232,6 +2231,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                return -1;
 
        previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
+
        for (i = 0; i < entries; i++) {
                struct cache_entry *ce = cache[i];
                if (ce->ce_flags & CE_REMOVE)
@@ -2247,15 +2247,21 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                        if (allow)
                                warning(msg, ce->name);
                        else
-                               return error(msg, ce->name);
+                               err = error(msg, ce->name);
 
                        drop_cache_tree = 1;
                }
-               if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
-                       return -1;
+               if (ce_write_entry(&c, newfd, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0)
+                       err = -1;
+
+               if (err)
+                       break;
        }
        strbuf_release(&previous_name_buf);
 
+       if (err)
+               return err;
+
        /* Write extension data here */
        if (!strip_extensions && istate->split_index) {
                struct strbuf sb = STRBUF_INIT;
@@ -2303,8 +2309,11 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
 
        if (ce_flush(&c, newfd, istate->sha1))
                return -1;
-       if (close_tempfile(tempfile))
-               return error(_("could not close '%s'"), tempfile->filename.buf);
+       if (close_tempfile_gently(tempfile)) {
+               error(_("could not close '%s'"), tempfile->filename.buf);
+               delete_tempfile(&tempfile);
+               return -1;
+       }
        if (stat(tempfile->filename.buf, &st))
                return -1;
        istate->timestamp.sec = (unsigned int)st.st_mtime;
@@ -2328,7 +2337,7 @@ static int commit_locked_index(struct lock_file *lk)
 static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
                                 unsigned flags)
 {
-       int ret = do_write_index(istate, &lock->tempfile, 0);
+       int ret = do_write_index(istate, lock->tempfile, 0);
        if (ret)
                return ret;
        assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
@@ -2336,7 +2345,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
        if (flags & COMMIT_LOCK)
                return commit_locked_index(lock);
        else if (flags & CLOSE_LOCK)
-               return close_lock_file(lock);
+               return close_lock_file_gently(lock);
        else
                return ret;
 }
@@ -2411,34 +2420,33 @@ static int clean_shared_index_files(const char *current_hex)
        return 0;
 }
 
-static struct tempfile temporary_sharedindex;
-
 static int write_shared_index(struct index_state *istate,
                              struct lock_file *lock, unsigned flags)
 {
+       struct tempfile *temp;
        struct split_index *si = istate->split_index;
-       int fd, ret;
+       int ret;
 
-       fd = mks_tempfile(&temporary_sharedindex, git_path("sharedindex_XXXXXX"));
-       if (fd < 0) {
+       temp = mks_tempfile(git_path("sharedindex_XXXXXX"));
+       if (!temp) {
                hashclr(si->base_sha1);
                return do_write_locked_index(istate, lock, flags);
        }
        move_cache_to_base_index(istate);
-       ret = do_write_index(si->base, &temporary_sharedindex, 1);
+       ret = do_write_index(si->base, temp, 1);
        if (ret) {
-               delete_tempfile(&temporary_sharedindex);
+               delete_tempfile(&temp);
                return ret;
        }
-       ret = adjust_shared_perm(get_tempfile_path(&temporary_sharedindex));
+       ret = adjust_shared_perm(get_tempfile_path(temp));
        if (ret) {
                int save_errno = errno;
-               error("cannot fix permission bits on %s", get_tempfile_path(&temporary_sharedindex));
-               delete_tempfile(&temporary_sharedindex);
+               error("cannot fix permission bits on %s", get_tempfile_path(temp));
+               delete_tempfile(&temp);
                errno = save_errno;
                return ret;
        }
-       ret = rename_tempfile(&temporary_sharedindex,
+       ret = rename_tempfile(&temp,
                              git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
        if (!ret) {
                hashcpy(si->base_sha1, si->base->sha1);
diff --git a/refs.c b/refs.c
index 3d549a89701ab77c973835cd7c556b5a8c6ddd6f..a2b97fba780debe440203ff78f8203b5d140d3c2 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -336,12 +336,6 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_tag_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_tag_ref(get_submodule_ref_store(submodule),
-                                    fn, cb_data);
-}
-
 int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
@@ -352,12 +346,6 @@ int for_each_branch_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_branch_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_branch_ref(get_submodule_ref_store(submodule),
-                                       fn, cb_data);
-}
-
 int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
@@ -368,12 +356,6 @@ int for_each_remote_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_remote_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
-                                       fn, cb_data);
-}
-
 int head_ref_namespaced(each_ref_fn fn, void *cb_data)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -579,6 +561,21 @@ enum ref_type ref_type(const char *refname)
        return REF_TYPE_NORMAL;
 }
 
+long get_files_ref_lock_timeout_ms(void)
+{
+       static int configured = 0;
+
+       /* The default timeout is 100 ms: */
+       static int timeout_ms = 100;
+
+       if (!configured) {
+               git_config_get_int("core.filesreflocktimeout", &timeout_ms);
+               configured = 1;
+       }
+
+       return timeout_ms;
+}
+
 static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
                           const unsigned char *old_sha1, struct strbuf *err)
 {
@@ -591,7 +588,9 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
        strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
 
        filename = git_path("%s", pseudoref);
-       fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+       fd = hold_lock_file_for_update_timeout(&lock, filename,
+                                              LOCK_DIE_ON_ERROR,
+                                              get_files_ref_lock_timeout_ms());
        if (fd < 0) {
                strbuf_addf(err, "could not open '%s' for writing: %s",
                            filename, strerror(errno));
@@ -634,8 +633,9 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1
                int fd;
                unsigned char actual_old_sha1[20];
 
-               fd = hold_lock_file_for_update(&lock, filename,
-                                              LOCK_DIE_ON_ERROR);
+               fd = hold_lock_file_for_update_timeout(
+                               &lock, filename, LOCK_DIE_ON_ERROR,
+                               get_files_ref_lock_timeout_ms());
                if (fd < 0)
                        die_errno(_("Could not open '%s' for writing"), filename);
                if (read_ref(pseudoref, actual_old_sha1))
@@ -1248,19 +1248,13 @@ int refs_rename_ref_available(struct ref_store *refs,
        return ok;
 }
 
-int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        struct object_id oid;
        int flag;
 
-       if (submodule) {
-               if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
-                       return fn("HEAD", &oid, 0, cb_data);
-
-               return 0;
-       }
-
-       if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
+       if (!refs_read_ref_full(refs, "HEAD", RESOLVE_REF_READING,
+                               oid.hash, &flag))
                return fn("HEAD", &oid, flag, cb_data);
 
        return 0;
@@ -1268,7 +1262,7 @@ int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 
 int head_ref(each_ref_fn fn, void *cb_data)
 {
-       return head_ref_submodule(NULL, fn, cb_data);
+       return refs_head_ref(get_main_ref_store(), fn, cb_data);
 }
 
 struct ref_iterator *refs_ref_iterator_begin(
@@ -1326,11 +1320,6 @@ int for_each_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_ref(get_submodule_ref_store(submodule), fn, cb_data);
-}
-
 int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
                         each_ref_fn fn, void *cb_data)
 {
@@ -1352,23 +1341,15 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig
                               prefix, fn, 0, flag, cb_data);
 }
 
-int for_each_ref_in_submodule(const char *submodule, const char *prefix,
-                             each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_ref_in(get_submodule_ref_store(submodule),
-                                   prefix, fn, cb_data);
-}
-
-int for_each_fullref_in_submodule(const char *submodule, const char *prefix,
-                                 each_ref_fn fn, void *cb_data,
-                                 unsigned int broken)
+int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+                            each_ref_fn fn, void *cb_data,
+                            unsigned int broken)
 {
        unsigned int flag = 0;
 
        if (broken)
                flag = DO_FOR_EACH_INCLUDE_BROKEN;
-       return do_for_each_ref(get_submodule_ref_store(submodule),
-                              prefix, fn, 0, flag, cb_data);
+       return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
 }
 
 int for_each_replace_ref(each_ref_fn fn, void *cb_data)
@@ -1503,25 +1484,10 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 int resolve_gitlink_ref(const char *submodule, const char *refname,
                        unsigned char *sha1)
 {
-       size_t len = strlen(submodule);
        struct ref_store *refs;
        int flags;
 
-       while (len && submodule[len - 1] == '/')
-               len--;
-
-       if (!len)
-               return -1;
-
-       if (submodule[len]) {
-               /* We need to strip off one or more trailing slashes */
-               char *stripped = xmemdupz(submodule, len);
-
-               refs = get_submodule_ref_store(stripped);
-               free(stripped);
-       } else {
-               refs = get_submodule_ref_store(submodule);
-       }
+       refs = get_submodule_ref_store(submodule);
 
        if (!refs)
                return -1;
@@ -1636,31 +1602,32 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
 {
        struct strbuf submodule_sb = STRBUF_INIT;
        struct ref_store *refs;
-       int ret;
+       char *to_free = NULL;
+       size_t len;
 
-       if (!submodule || !*submodule) {
-               /*
-                * FIXME: This case is ideally not allowed. But that
-                * can't happen until we clean up all the callers.
-                */
-               return get_main_ref_store();
-       }
+       if (!submodule)
+               return NULL;
+
+       len = strlen(submodule);
+       while (len && is_dir_sep(submodule[len - 1]))
+               len--;
+       if (!len)
+               return NULL;
+
+       if (submodule[len])
+               /* We need to strip off one or more trailing slashes */
+               submodule = to_free = xmemdupz(submodule, len);
 
        refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
        if (refs)
-               return refs;
+               goto done;
 
        strbuf_addstr(&submodule_sb, submodule);
-       ret = is_nonbare_repository_dir(&submodule_sb);
-       strbuf_release(&submodule_sb);
-       if (!ret)
-               return NULL;
+       if (!is_nonbare_repository_dir(&submodule_sb))
+               goto done;
 
-       ret = submodule_to_gitdir(&submodule_sb, submodule);
-       if (ret) {
-               strbuf_release(&submodule_sb);
-               return NULL;
-       }
+       if (submodule_to_gitdir(&submodule_sb, submodule))
+               goto done;
 
        /* assume that add_submodule_odb() has been called */
        refs = ref_store_init(submodule_sb.buf,
@@ -1668,7 +1635,10 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
        register_ref_store_map(&submodule_ref_stores, "submodule",
                               refs, submodule);
 
+done:
        strbuf_release(&submodule_sb);
+       free(to_free);
+
        return refs;
 }
 
diff --git a/refs.h b/refs.h
index 6daa78eb50ddc195c6d5bfb568d0b05ab08f7923..78a26400b61b0046c72a64b985b9aa6443f27021 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -275,6 +275,8 @@ typedef int each_ref_fn(const char *refname,
  * modifies the reference also returns a nonzero value to immediately
  * stop the iteration. Returned references are sorted.
  */
+int refs_head_ref(struct ref_store *refs,
+                 each_ref_fn fn, void *cb_data);
 int refs_for_each_ref(struct ref_store *refs,
                      each_ref_fn fn, void *cb_data);
 int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
@@ -289,6 +291,9 @@ int refs_for_each_remote_ref(struct ref_store *refs,
 int head_ref(each_ref_fn fn, void *cb_data);
 int for_each_ref(each_ref_fn fn, void *cb_data);
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
+int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+                            each_ref_fn fn, void *cb_data,
+                            unsigned int broken);
 int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
                        unsigned int broken);
 int for_each_tag_ref(each_ref_fn fn, void *cb_data);
@@ -299,21 +304,6 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
                         const char *prefix, void *cb_data);
 
-int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
-int for_each_ref_submodule(const char *submodule,
-                          each_ref_fn fn, void *cb_data);
-int for_each_ref_in_submodule(const char *submodule, const char *prefix,
-                             each_ref_fn fn, void *cb_data);
-int for_each_fullref_in_submodule(const char *submodule, const char *prefix,
-                                 each_ref_fn fn, void *cb_data,
-                                 unsigned int broken);
-int for_each_tag_ref_submodule(const char *submodule,
-                              each_ref_fn fn, void *cb_data);
-int for_each_branch_ref_submodule(const char *submodule,
-                                 each_ref_fn fn, void *cb_data);
-int for_each_remote_ref_submodule(const char *submodule,
-                                 each_ref_fn fn, void *cb_data);
-
 int head_ref_namespaced(each_ref_fn fn, void *cb_data);
 int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
 
index 5cca55510b6a0683f16937215f6056d896c622e0..a7cc65d0dee2dc5e8d3af6e447fc337e5958930f 100644 (file)
@@ -12,7 +12,7 @@
 
 struct ref_lock {
        char *ref_name;
-       struct lock_file *lk;
+       struct lock_file lk;
        struct object_id old_oid;
 };
 
@@ -106,15 +106,6 @@ static void files_reflog_path(struct files_ref_store *refs,
                              struct strbuf *sb,
                              const char *refname)
 {
-       if (!refname) {
-               /*
-                * FIXME: of course this is wrong in multi worktree
-                * setting. To be fixed real soon.
-                */
-               strbuf_addf(sb, "%s/logs", refs->gitcommondir);
-               return;
-       }
-
        switch (ref_type(refname)) {
        case REF_TYPE_PER_WORKTREE:
        case REF_TYPE_PSEUDOREF:
@@ -418,9 +409,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
 
 static void unlock_ref(struct ref_lock *lock)
 {
-       /* Do not free lock->lk -- atexit() still looks at them */
-       if (lock->lk)
-               rollback_lock_file(lock->lk);
+       rollback_lock_file(&lock->lk);
        free(lock->ref_name);
        free(lock);
 }
@@ -534,10 +523,9 @@ static int lock_raw_ref(struct files_ref_store *refs,
                goto error_return;
        }
 
-       if (!lock->lk)
-               lock->lk = xcalloc(1, sizeof(struct lock_file));
-
-       if (hold_lock_file_for_update(lock->lk, ref_file.buf, LOCK_NO_DEREF) < 0) {
+       if (hold_lock_file_for_update_timeout(
+                           &lock->lk, ref_file.buf, LOCK_NO_DEREF,
+                           get_files_ref_lock_timeout_ms()) < 0) {
                if (errno == ENOENT && --attempts_remaining > 0) {
                        /*
                         * Maybe somebody just deleted one of the
@@ -865,7 +853,9 @@ static int create_reflock(const char *path, void *cb)
 {
        struct lock_file *lk = cb;
 
-       return hold_lock_file_for_update(lk, path, LOCK_NO_DEREF) < 0 ? -1 : 0;
+       return hold_lock_file_for_update_timeout(
+                       lk, path, LOCK_NO_DEREF,
+                       get_files_ref_lock_timeout_ms()) < 0 ? -1 : 0;
 }
 
 /*
@@ -945,11 +935,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
                goto error_return;
        }
 
-       lock->lk = xcalloc(1, sizeof(struct lock_file));
-
        lock->ref_name = xstrdup(refname);
 
-       if (raceproof_create_file(ref_file.buf, create_reflock, lock->lk)) {
+       if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) {
                last_errno = errno;
                unable_to_lock_message(ref_file.buf, errno, err);
                goto error_return;
@@ -1398,16 +1386,16 @@ static int files_rename_ref(struct ref_store *ref_store,
        return ret;
 }
 
-static int close_ref(struct ref_lock *lock)
+static int close_ref_gently(struct ref_lock *lock)
 {
-       if (close_lock_file(lock->lk))
+       if (close_lock_file_gently(&lock->lk))
                return -1;
        return 0;
 }
 
 static int commit_ref(struct ref_lock *lock)
 {
-       char *path = get_locked_file_path(lock->lk);
+       char *path = get_locked_file_path(&lock->lk);
        struct stat st;
 
        if (!lstat(path, &st) && S_ISDIR(st.st_mode)) {
@@ -1431,7 +1419,7 @@ static int commit_ref(struct ref_lock *lock)
                free(path);
        }
 
-       if (commit_lock_file(lock->lk))
+       if (commit_lock_file(&lock->lk))
                return -1;
        return 0;
 }
@@ -1623,12 +1611,12 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
                unlock_ref(lock);
                return -1;
        }
-       fd = get_lock_file_fd(lock->lk);
+       fd = get_lock_file_fd(&lock->lk);
        if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
            write_in_full(fd, &term, 1) != 1 ||
-           close_ref(lock) < 0) {
+           close_ref_gently(lock) < 0) {
                strbuf_addf(err,
-                           "couldn't write '%s'", get_lock_file_path(lock->lk));
+                           "couldn't write '%s'", get_lock_file_path(&lock->lk));
                unlock_ref(lock);
                return -1;
        }
@@ -1705,7 +1693,7 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
 {
        int ret = -1;
 #ifndef NO_SYMLINK_HEAD
-       char *ref_path = get_locked_file_path(lock->lk);
+       char *ref_path = get_locked_file_path(&lock->lk);
        unlink(ref_path);
        ret = symlink(target, ref_path);
        free(ref_path);
@@ -1741,14 +1729,14 @@ static int create_symref_locked(struct files_ref_store *refs,
                return 0;
        }
 
-       if (!fdopen_lock_file(lock->lk, "w"))
+       if (!fdopen_lock_file(&lock->lk, "w"))
                return error("unable to fdopen %s: %s",
-                            lock->lk->tempfile.filename.buf, strerror(errno));
+                            lock->lk.tempfile->filename.buf, strerror(errno));
 
        update_symref_reflog(refs, lock, refname, target, logmsg);
 
        /* no error check; commit_ref will check ferror */
-       fprintf(lock->lk->tempfile.fp, "ref: %s\n", target);
+       fprintf(lock->lk.tempfile->fp, "ref: %s\n", target);
        if (commit_ref(lock) < 0)
                return error("unable to write symref for %s: %s", refname,
                             strerror(errno));
@@ -2055,23 +2043,63 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
        files_reflog_iterator_abort
 };
 
-static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
+static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
+                                                 const char *gitdir)
 {
-       struct files_ref_store *refs =
-               files_downcast(ref_store, REF_STORE_READ,
-                              "reflog_iterator_begin");
        struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
        struct ref_iterator *ref_iterator = &iter->base;
        struct strbuf sb = STRBUF_INIT;
 
        base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
-       files_reflog_path(refs, &sb, NULL);
+       strbuf_addf(&sb, "%s/logs", gitdir);
        iter->dir_iterator = dir_iterator_begin(sb.buf);
        iter->ref_store = ref_store;
        strbuf_release(&sb);
+
        return ref_iterator;
 }
 
+static enum iterator_selection reflog_iterator_select(
+       struct ref_iterator *iter_worktree,
+       struct ref_iterator *iter_common,
+       void *cb_data)
+{
+       if (iter_worktree) {
+               /*
+                * We're a bit loose here. We probably should ignore
+                * common refs if they are accidentally added as
+                * per-worktree refs.
+                */
+               return ITER_SELECT_0;
+       } else if (iter_common) {
+               if (ref_type(iter_common->refname) == REF_TYPE_NORMAL)
+                       return ITER_SELECT_1;
+
+               /*
+                * The main ref store may contain main worktree's
+                * per-worktree refs, which should be ignored
+                */
+               return ITER_SKIP_1;
+       } else
+               return ITER_DONE;
+}
+
+static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
+{
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ,
+                              "reflog_iterator_begin");
+
+       if (!strcmp(refs->gitdir, refs->gitcommondir)) {
+               return reflog_iterator_begin(ref_store, refs->gitcommondir);
+       } else {
+               return merge_ref_iterator_begin(
+                       reflog_iterator_begin(ref_store, refs->gitdir),
+                       reflog_iterator_begin(ref_store, refs->gitcommondir),
+                       reflog_iterator_select, refs);
+       }
+}
+
 /*
  * If update is a direct update of head_ref (the reference pointed to
  * by HEAD), then add an extra REF_LOG_ONLY update for HEAD.
@@ -2095,11 +2123,10 @@ static int split_head_update(struct ref_update *update,
 
        /*
         * First make sure that HEAD is not already in the
-        * transaction. This insertion is O(N) in the transaction
+        * transaction. This check is O(lg N) in the transaction
         * size, but it happens at most once per transaction.
         */
-       item = string_list_insert(affected_refnames, "HEAD");
-       if (item->util) {
+       if (string_list_has_string(affected_refnames, "HEAD")) {
                /* An entry already existed */
                strbuf_addf(err,
                            "multiple updates for 'HEAD' (including one "
@@ -2114,6 +2141,14 @@ static int split_head_update(struct ref_update *update,
                        update->new_oid.hash, update->old_oid.hash,
                        update->msg);
 
+       /*
+        * Add "HEAD". This insertion is O(N) in the transaction
+        * size, but it happens at most once per transaction.
+        * Add new_update->refname instead of a literal "HEAD".
+        */
+       if (strcmp(new_update->refname, "HEAD"))
+               BUG("%s unexpectedly not 'HEAD'", new_update->refname);
+       item = string_list_insert(affected_refnames, new_update->refname);
        item->util = new_update;
 
        return 0;
@@ -2140,13 +2175,12 @@ static int split_symref_update(struct files_ref_store *refs,
 
        /*
         * First make sure that referent is not already in the
-        * transaction. This insertion is O(N) in the transaction
+        * transaction. This check is O(lg N) in the transaction
         * size, but it happens at most once per symref in a
         * transaction.
         */
-       item = string_list_insert(affected_refnames, referent);
-       if (item->util) {
-               /* An entry already existed */
+       if (string_list_has_string(affected_refnames, referent)) {
+               /* An entry already exists */
                strbuf_addf(err,
                            "multiple updates for '%s' (including one "
                            "via symref '%s') are not allowed",
@@ -2181,6 +2215,17 @@ static int split_symref_update(struct files_ref_store *refs,
        update->flags |= REF_LOG_ONLY | REF_NODEREF;
        update->flags &= ~REF_HAVE_OLD;
 
+       /*
+        * Add the referent. This insertion is O(N) in the transaction
+        * size, but it happens at most once per symref in a
+        * transaction. Make sure to add new_update->refname, which will
+        * be valid as long as affected_refnames is in use, and NOT
+        * referent, which might soon be freed by our caller.
+        */
+       item = string_list_insert(affected_refnames, new_update->refname);
+       if (item->util)
+               BUG("%s unexpectedly found in affected_refnames",
+                   new_update->refname);
        item->util = new_update;
 
        return 0;
@@ -2252,7 +2297,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
        struct strbuf referent = STRBUF_INIT;
        int mustexist = (update->flags & REF_HAVE_OLD) &&
                !is_null_oid(&update->old_oid);
-       int ret;
+       int ret = 0;
        struct ref_lock *lock;
 
        files_assert_main_repository(refs, "lock_ref_for_update");
@@ -2264,7 +2309,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                ret = split_head_update(update, transaction, head_ref,
                                        affected_refnames, err);
                if (ret)
-                       return ret;
+                       goto out;
        }
 
        ret = lock_raw_ref(refs, update->refname, mustexist,
@@ -2278,7 +2323,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                strbuf_addf(err, "cannot lock ref '%s': %s",
                            original_update_refname(update), reason);
                free(reason);
-               return ret;
+               goto out;
        }
 
        update->backend_data = lock;
@@ -2297,10 +2342,12 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                                        strbuf_addf(err, "cannot lock ref '%s': "
                                                    "error reading reference",
                                                    original_update_refname(update));
-                                       return -1;
+                                       ret = TRANSACTION_GENERIC_ERROR;
+                                       goto out;
                                }
                        } else if (check_old_oid(update, &lock->old_oid, err)) {
-                               return TRANSACTION_GENERIC_ERROR;
+                               ret = TRANSACTION_GENERIC_ERROR;
+                               goto out;
                        }
                } else {
                        /*
@@ -2314,13 +2361,15 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                                                  referent.buf, transaction,
                                                  affected_refnames, err);
                        if (ret)
-                               return ret;
+                               goto out;
                }
        } else {
                struct ref_update *parent_update;
 
-               if (check_old_oid(update, &lock->old_oid, err))
-                       return TRANSACTION_GENERIC_ERROR;
+               if (check_old_oid(update, &lock->old_oid, err)) {
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
+               }
 
                /*
                 * If this update is happening indirectly because of a
@@ -2357,7 +2406,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                                    "cannot update ref '%s': %s",
                                    update->refname, write_err);
                        free(write_err);
-                       return TRANSACTION_GENERIC_ERROR;
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
                } else {
                        update->flags |= REF_NEEDS_COMMIT;
                }
@@ -2368,13 +2418,17 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                 * the lockfile is still open. Close it to
                 * free up the file descriptor:
                 */
-               if (close_ref(lock)) {
+               if (close_ref_gently(lock)) {
                        strbuf_addf(err, "couldn't close '%s.lock'",
                                    update->refname);
-                       return TRANSACTION_GENERIC_ERROR;
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
                }
        }
-       return 0;
+
+out:
+       strbuf_release(&referent);
+       return ret;
 }
 
 /*
@@ -2844,16 +2898,17 @@ static int files_reflog_expire(struct ref_store *ref_store,
                        !(type & REF_ISSYMREF) &&
                        !is_null_oid(&cb.last_kept_oid);
 
-               if (close_lock_file(&reflog_lock)) {
+               if (close_lock_file_gently(&reflog_lock)) {
                        status |= error("couldn't write %s: %s", log_file,
                                        strerror(errno));
+                       rollback_lock_file(&reflog_lock);
                } else if (update &&
-                          (write_in_full(get_lock_file_fd(lock->lk),
+                          (write_in_full(get_lock_file_fd(&lock->lk),
                                oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
-                           write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
-                           close_ref(lock) < 0)) {
+                           write_str_in_full(get_lock_file_fd(&lock->lk), "\n") != 1 ||
+                           close_ref_gently(lock) < 0)) {
                        status |= error("couldn't write %s",
-                                       get_lock_file_path(lock->lk));
+                                       get_lock_file_path(&lock->lk));
                        rollback_lock_file(&reflog_lock);
                } else if (commit_lock_file(&reflog_lock)) {
                        status |= error("unable to write reflog '%s' (%s)",
index 412c85034fc3b8632f16d13c4d467ad41060ddeb..321608a11472821ee69ef38a309d497ac92594df 100644 (file)
@@ -75,7 +75,7 @@ struct packed_ref_store {
         * "packed-refs" file. Note that this (and thus the enclosing
         * `packed_ref_store`) must not be freed.
         */
-       struct tempfile tempfile;
+       struct tempfile *tempfile;
 };
 
 struct ref_store *packed_ref_store_create(const char *path,
@@ -545,8 +545,9 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
                return -1;
        }
 
-       if (close_lock_file(&refs->lock)) {
+       if (close_lock_file_gently(&refs->lock)) {
                strbuf_addf(err, "unable to close %s: %s", refs->path, strerror(errno));
+               rollback_lock_file(&refs->lock);
                return -1;
        }
 
@@ -627,7 +628,8 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
         */
        packed_refs_path = get_locked_file_path(&refs->lock);
        strbuf_addf(&sb, "%s.new", packed_refs_path);
-       if (create_tempfile(&refs->tempfile, sb.buf) < 0) {
+       refs->tempfile = create_tempfile(sb.buf);
+       if (!refs->tempfile) {
                strbuf_addf(err, "unable to create file %s: %s",
                            sb.buf, strerror(errno));
                strbuf_release(&sb);
@@ -635,7 +637,7 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
        }
        strbuf_release(&sb);
 
-       out = fdopen_tempfile(&refs->tempfile, "w");
+       out = fdopen_tempfile(refs->tempfile, "w");
        if (!out) {
                strbuf_addf(err, "unable to fdopen packed-refs tempfile: %s",
                            strerror(errno));
@@ -644,7 +646,7 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
 
        if (fprintf(out, "%s", PACKED_REFS_HEADER) < 0) {
                strbuf_addf(err, "error writing to %s: %s",
-                           get_tempfile_path(&refs->tempfile), strerror(errno));
+                           get_tempfile_path(refs->tempfile), strerror(errno));
                goto error;
        }
 
@@ -656,7 +658,7 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
                if (write_packed_entry(out, iter->refname, iter->oid->hash,
                                       peel_error ? NULL : peeled.hash)) {
                        strbuf_addf(err, "error writing to %s: %s",
-                                   get_tempfile_path(&refs->tempfile),
+                                   get_tempfile_path(refs->tempfile),
                                    strerror(errno));
                        ref_iterator_abort(iter);
                        goto error;
index 4789106fc093391f1c3e67d0fe46c8ece47fde34..b02dc5a7e3500cae1784199a82b37be020004df1 100644 (file)
  */
 #define REF_DELETED_LOOSE 0x200
 
+/*
+ * Return the length of time to retry acquiring a loose reference lock
+ * before giving up, in milliseconds:
+ */
+long get_files_ref_lock_timeout_ms(void);
+
 /*
  * Return true iff refname is minimally safe. "Safe" here means that
  * deleting a loose reference by this name will not do any damage, for
index 43c317e4e9f6e67aa3e1d0ae245d097bdba664db..41130900698893f46dd18b817a7e6b6485c735a4 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -2084,7 +2084,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                                _("  (use \"git branch --unset-upstream\" to fixup)\n"));
        } else if (!ours && !theirs) {
                strbuf_addf(sb,
-                       _("Your branch is up-to-date with '%s'.\n"),
+                       _("Your branch is up to date with '%s'.\n"),
                        base);
        } else if (!theirs) {
                strbuf_addf(sb,
index 70634d456cc0b70cdbac1579b82016761252c7d9..d77235645ea88ff9a51346436fcaac69dabfaaeb 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -1133,14 +1133,14 @@ int rerere_forget(struct pathspec *pathspec)
  * Garbage collection support
  */
 
-static time_t rerere_created_at(struct rerere_id *id)
+static timestamp_t rerere_created_at(struct rerere_id *id)
 {
        struct stat st;
 
        return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
 }
 
-static time_t rerere_last_used_at(struct rerere_id *id)
+static timestamp_t rerere_last_used_at(struct rerere_id *id)
 {
        struct stat st;
 
@@ -1157,11 +1157,11 @@ static void unlink_rr_item(struct rerere_id *id)
        id->collection->status[id->variant] = 0;
 }
 
-static void prune_one(struct rerere_id *id, time_t now,
-                     int cutoff_resolve, int cutoff_noresolve)
+static void prune_one(struct rerere_id *id,
+                     timestamp_t cutoff_resolve, timestamp_t cutoff_noresolve)
 {
-       time_t then;
-       int cutoff;
+       timestamp_t then;
+       timestamp_t cutoff;
 
        then = rerere_last_used_at(id);
        if (then)
@@ -1172,7 +1172,7 @@ static void prune_one(struct rerere_id *id, time_t now,
                        return;
                cutoff = cutoff_noresolve;
        }
-       if (then < now - cutoff * 86400)
+       if (then < cutoff)
                unlink_rr_item(id);
 }
 
@@ -1182,15 +1182,15 @@ void rerere_gc(struct string_list *rr)
        DIR *dir;
        struct dirent *e;
        int i;
-       time_t now = time(NULL);
-       int cutoff_noresolve = 15;
-       int cutoff_resolve = 60;
+       timestamp_t now = time(NULL);
+       timestamp_t cutoff_noresolve = now - 15 * 86400;
+       timestamp_t cutoff_resolve = now - 60 * 86400;
 
        if (setup_rerere(rr, 0) < 0)
                return;
 
-       git_config_get_int("gc.rerereresolved", &cutoff_resolve);
-       git_config_get_int("gc.rerereunresolved", &cutoff_noresolve);
+       git_config_get_expiry_in_days("gc.rerereresolved", &cutoff_resolve, now);
+       git_config_get_expiry_in_days("gc.rerereunresolved", &cutoff_noresolve, now);
        git_config(git_default_config, NULL);
        dir = opendir(git_path("rr-cache"));
        if (!dir)
@@ -1211,7 +1211,7 @@ void rerere_gc(struct string_list *rr)
                for (id.variant = 0, id.collection = rr_dir;
                     id.variant < id.collection->status_nr;
                     id.variant++) {
-                       prune_one(&id, now, cutoff_resolve, cutoff_noresolve);
+                       prune_one(&id, cutoff_resolve, cutoff_noresolve);
                        if (id.collection->status[id.variant])
                                now_empty = 0;
                }
index 94a5e98525187a2d7f1c6f4e9a22f4cb3bb0cf84..f9a90d71d2008415e80b30c9e5d3b2743af5438f 100644 (file)
@@ -20,6 +20,7 @@
 #include "cache-tree.h"
 #include "bisect.h"
 #include "packfile.h"
+#include "worktree.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -1132,6 +1133,7 @@ struct all_refs_cb {
        int warned_bad_reflog;
        struct rev_info *all_revs;
        const char *name_for_errormsg;
+       struct ref_store *refs;
 };
 
 int ref_excluded(struct string_list *ref_excludes, const char *path)
@@ -1168,6 +1170,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
        cb->all_revs = revs;
        cb->all_flags = flags;
        revs->rev_input_given = 1;
+       cb->refs = NULL;
 }
 
 void clear_ref_exclusion(struct string_list **ref_excludes_p)
@@ -1188,12 +1191,19 @@ void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
        string_list_append(*ref_excludes_p, exclude);
 }
 
-static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
-               int (*for_each)(const char *, each_ref_fn, void *))
+static void handle_refs(struct ref_store *refs,
+                       struct rev_info *revs, unsigned flags,
+                       int (*for_each)(struct ref_store *, each_ref_fn, void *))
 {
        struct all_refs_cb cb;
+
+       if (!refs) {
+               /* this could happen with uninitialized submodules */
+               return;
+       }
+
        init_all_refs_cb(&cb, revs, flags);
-       for_each(submodule, handle_one_ref, &cb);
+       for_each(refs, handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
@@ -1229,17 +1239,41 @@ static int handle_one_reflog(const char *path, const struct object_id *oid,
        struct all_refs_cb *cb = cb_data;
        cb->warned_bad_reflog = 0;
        cb->name_for_errormsg = path;
-       for_each_reflog_ent(path, handle_one_reflog_ent, cb_data);
+       refs_for_each_reflog_ent(cb->refs, path,
+                                handle_one_reflog_ent, cb_data);
        return 0;
 }
 
+static void add_other_reflogs_to_pending(struct all_refs_cb *cb)
+{
+       struct worktree **worktrees, **p;
+
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+
+               if (wt->is_current)
+                       continue;
+
+               cb->refs = get_worktree_ref_store(wt);
+               refs_for_each_reflog(cb->refs,
+                                    handle_one_reflog,
+                                    cb);
+       }
+       free_worktrees(worktrees);
+}
+
 void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
 {
        struct all_refs_cb cb;
 
        cb.all_revs = revs;
        cb.all_flags = flags;
+       cb.refs = get_main_ref_store();
        for_each_reflog(handle_one_reflog, &cb);
+
+       if (!revs->single_worktree)
+               add_other_reflogs_to_pending(&cb);
 }
 
 static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
@@ -1263,13 +1297,13 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
 
 }
 
-void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
+static void do_add_index_objects_to_pending(struct rev_info *revs,
+                                           struct index_state *istate)
 {
        int i;
 
-       read_cache();
-       for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
                struct blob *blob;
 
                if (S_ISGITLINK(ce->ce_mode))
@@ -1282,13 +1316,39 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
                                             ce->ce_mode, ce->name);
        }
 
-       if (active_cache_tree) {
+       if (istate->cache_tree) {
                struct strbuf path = STRBUF_INIT;
-               add_cache_tree(active_cache_tree, revs, &path);
+               add_cache_tree(istate->cache_tree, revs, &path);
                strbuf_release(&path);
        }
 }
 
+void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
+{
+       struct worktree **worktrees, **p;
+
+       read_cache();
+       do_add_index_objects_to_pending(revs, &the_index);
+
+       if (revs->single_worktree)
+               return;
+
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+               struct index_state istate = { NULL };
+
+               if (wt->is_current)
+                       continue; /* current index already taken care of */
+
+               if (read_index_from(&istate,
+                                   worktree_git_path(wt, "index")) > 0)
+                       do_add_index_objects_to_pending(revs, &istate);
+               discard_index(&istate);
+       }
+       free_worktrees(worktrees);
+}
+
 static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
                            int exclude_parent)
 {
@@ -2069,23 +2129,25 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
        ctx->argc -= n;
 }
 
-static int for_each_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data, const char *term) {
+static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
+                              void *cb_data, const char *term)
+{
        struct strbuf bisect_refs = STRBUF_INIT;
        int status;
        strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
-       status = for_each_fullref_in_submodule(submodule, bisect_refs.buf, fn, cb_data, 0);
+       status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data, 0);
        strbuf_release(&bisect_refs);
        return status;
 }
 
-static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+static int for_each_bad_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       return for_each_bisect_ref(submodule, fn, cb_data, term_bad);
+       return for_each_bisect_ref(refs, fn, cb_data, term_bad);
 }
 
-static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+static int for_each_good_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       return for_each_bisect_ref(submodule, fn, cb_data, term_good);
+       return for_each_bisect_ref(refs, fn, cb_data, term_good);
 }
 
 static int handle_revision_pseudo_opt(const char *submodule,
@@ -2094,8 +2156,22 @@ static int handle_revision_pseudo_opt(const char *submodule,
 {
        const char *arg = argv[0];
        const char *optarg;
+       struct ref_store *refs;
        int argcount;
 
+       if (submodule) {
+               /*
+                * We need some something like get_submodule_worktrees()
+                * before we can go through all worktrees of a submodule,
+                * .e.g with adding all HEADs from --all, which is not
+                * supported right now, so stick to single worktree.
+                */
+               if (!revs->single_worktree)
+                       die("BUG: --single-worktree cannot be used together with submodule");
+               refs = get_submodule_ref_store(submodule);
+       } else
+               refs = get_main_ref_store();
+
        /*
         * NOTE!
         *
@@ -2107,22 +2183,29 @@ static int handle_revision_pseudo_opt(const char *submodule,
         * register it in the list at the top of handle_revision_opt.
         */
        if (!strcmp(arg, "--all")) {
-               handle_refs(submodule, revs, *flags, for_each_ref_submodule);
-               handle_refs(submodule, revs, *flags, head_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_ref);
+               handle_refs(refs, revs, *flags, refs_head_ref);
+               if (!revs->single_worktree) {
+                       struct all_refs_cb cb;
+
+                       init_all_refs_cb(&cb, revs, *flags);
+                       other_head_refs(handle_one_ref, &cb);
+               }
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--branches")) {
-               handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--bisect")) {
                read_bisect_terms(&term_bad, &term_good);
-               handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
-               handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
+               handle_refs(refs, revs, *flags, for_each_bad_bisect_ref);
+               handle_refs(refs, revs, *flags ^ (UNINTERESTING | BOTTOM),
+                           for_each_good_bisect_ref);
                revs->bisect = 1;
        } else if (!strcmp(arg, "--tags")) {
-               handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_tag_ref);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--remotes")) {
-               handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
                struct all_refs_cb cb;
@@ -2169,6 +2252,8 @@ static int handle_revision_pseudo_opt(const char *submodule,
                        return error("invalid argument to --no-walk");
        } else if (!strcmp(arg, "--do-walk")) {
                revs->no_walk = 0;
+       } else if (!strcmp(arg, "--single-worktree")) {
+               revs->single_worktree = 1;
        } else {
                return 0;
        }
index bc18487d6fff5e86dff60748c084995a77d2b588..3a3d3e2cf824bf0ae404204c1ea81dad813d5f93 100644 (file)
@@ -96,6 +96,7 @@ struct rev_info {
                        topo_order:1,
                        simplify_merges:1,
                        simplify_by_decoration:1,
+                       single_worktree:1,
                        tag_objects:1,
                        tree_objects:1,
                        blob_objects:1,
index 20af2856818ed51b2afb1718a7e317133ee0d7bd..cf5314f402ce78f0d5ab2bd72ee7f334b6394e04 100644 (file)
@@ -7,10 +7,4 @@ extern int sha1_pos(const unsigned char *sha1,
                    void *table,
                    size_t nr,
                    sha1_access_fn fn);
-
-extern int sha1_entry_pos(const void *table,
-                         size_t elem_size,
-                         size_t key_offset,
-                         unsigned lo, unsigned hi, unsigned nr,
-                         const unsigned char *key);
 #endif
index bb0831b4c803919e90ce263c4aed31d7fedd4620..5f71bbac3ea9a11a4369ec0353e2e1258979ba19 100644 (file)
@@ -30,7 +30,7 @@
 #include "quote.h"
 #include "packfile.h"
 
-const unsigned char null_sha1[20];
+const unsigned char null_sha1[GIT_MAX_RAWSZ];
 const struct object_id null_oid;
 const struct object_id empty_tree_oid = {
        EMPTY_TREE_SHA1_BIN_LITERAL
@@ -1581,7 +1581,7 @@ int write_sha1_file(const void *buf, unsigned long len, const char *type, unsign
 }
 
 int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type,
-                            unsigned char *sha1, unsigned flags)
+                            struct object_id *oid, unsigned flags)
 {
        char *header;
        int hdrlen, status = 0;
@@ -1589,13 +1589,13 @@ int hash_sha1_file_literally(const void *buf, unsigned long len, const char *typ
        /* type string, SP, %lu of the length plus NUL must fit this */
        hdrlen = strlen(type) + 32;
        header = xmalloc(hdrlen);
-       write_sha1_file_prepare(buf, len, type, sha1, header, &hdrlen);
+       write_sha1_file_prepare(buf, len, type, oid->hash, header, &hdrlen);
 
        if (!(flags & HASH_WRITE_OBJECT))
                goto cleanup;
-       if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
+       if (freshen_packed_object(oid->hash) || freshen_loose_object(oid->hash))
                goto cleanup;
-       status = write_loose_object(sha1, header, hdrlen, buf, len, 0);
+       status = write_loose_object(oid->hash, header, hdrlen, buf, len, 0);
 
 cleanup:
        free(header);
@@ -1785,14 +1785,14 @@ static int index_core(unsigned char *sha1, int fd, size_t size,
  * binary blobs, they generally do not want to get any conversion, and
  * callers should avoid this code path when filters are requested.
  */
-static int index_stream(unsigned char *sha1, int fd, size_t size,
+static int index_stream(struct object_id *oid, int fd, size_t size,
                        enum object_type type, const char *path,
                        unsigned flags)
 {
-       return index_bulk_checkin(sha1, fd, size, type, path, flags);
+       return index_bulk_checkin(oid->hash, fd, size, type, path, flags);
 }
 
-int index_fd(unsigned char *sha1, int fd, struct stat *st,
+int index_fd(struct object_id *oid, int fd, struct stat *st,
             enum object_type type, const char *path, unsigned flags)
 {
        int ret;
@@ -1802,21 +1802,21 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st,
         * die() for large files.
         */
        if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path))
-               ret = index_stream_convert_blob(sha1, fd, path, flags);
+               ret = index_stream_convert_blob(oid->hash, fd, path, flags);
        else if (!S_ISREG(st->st_mode))
-               ret = index_pipe(sha1, fd, type, path, flags);
+               ret = index_pipe(oid->hash, fd, type, path, flags);
        else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
                 (path && would_convert_to_git(&the_index, path)))
-               ret = index_core(sha1, fd, xsize_t(st->st_size), type, path,
+               ret = index_core(oid->hash, fd, xsize_t(st->st_size), type, path,
                                 flags);
        else
-               ret = index_stream(sha1, fd, xsize_t(st->st_size), type, path,
+               ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
                                   flags);
        close(fd);
        return ret;
 }
 
-int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags)
+int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags)
 {
        int fd;
        struct strbuf sb = STRBUF_INIT;
@@ -1826,7 +1826,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned
                fd = open(path, O_RDONLY);
                if (fd < 0)
                        return error_errno("open(\"%s\")", path);
-               if (index_fd(sha1, fd, st, OBJ_BLOB, path, flags) < 0)
+               if (index_fd(oid, fd, st, OBJ_BLOB, path, flags) < 0)
                        return error("%s: failed to insert into database",
                                     path);
                break;
@@ -1834,14 +1834,14 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned
                if (strbuf_readlink(&sb, path, st->st_size))
                        return error_errno("readlink(\"%s\")", path);
                if (!(flags & HASH_WRITE_OBJECT))
-                       hash_sha1_file(sb.buf, sb.len, blob_type, sha1);
-               else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1))
+                       hash_sha1_file(sb.buf, sb.len, blob_type, oid->hash);
+               else if (write_sha1_file(sb.buf, sb.len, blob_type, oid->hash))
                        return error("%s: failed to insert into database",
                                     path);
                strbuf_release(&sb);
                break;
        case S_IFDIR:
-               return resolve_gitlink_ref(path, "HEAD", sha1);
+               return resolve_gitlink_ref(path, "HEAD", oid->hash);
        default:
                return error("%s: unsupported file type", path);
        }
index 4d32b4f77e042f0da2b5181f753d0933ad438567..e0cc9d988c70337977853101c397960132af1981 100644 (file)
@@ -1,8 +1,19 @@
+#include "cache.h"
+
+#ifdef DC_SHA1_EXTERNAL
 /*
- * This code is included at the end of sha1dc/sha1.c with the
- * SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C macro.
+ * Same as SHA1DCInit, but with default save_hash=0
  */
+void git_SHA1DCInit(SHA1_CTX *ctx)
+{
+       SHA1DCInit(ctx);
+       SHA1DCSetSafeHash(ctx, 0);
+}
+#endif
 
+/*
+ * Same as SHA1DCFinal, but convert collision attack case into a verbose die().
+ */
 void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx)
 {
        if (!SHA1DCFinal(hash, ctx))
@@ -11,6 +22,9 @@ void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx)
            sha1_to_hex(hash));
 }
 
+/*
+ * Same as SHA1DCUpdate, but adjust types to match git's usual interface.
+ */
 void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *vdata, unsigned long len)
 {
        const char *data = vdata;
index a8a5c1da169e42360fa39851f27a8c4863d0e9ad..a8c272927842901190b07098911ef785a458bcf3 100644 (file)
@@ -1,19 +1,23 @@
-/*
- * This code is included at the end of sha1dc/sha1.h with the
- * SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H macro.
- */
+/* Plumbing with collition-detecting SHA1 code */
 
-/*
- * Same as SHA1DCFinal, but convert collision attack case into a verbose die().
- */
-void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *);
+#ifdef DC_SHA1_SUBMODULE
+#include "sha1collisiondetection/lib/sha1.h"
+#elif defined(DC_SHA1_EXTERNAL)
+#include <sha1dc/sha1.h>
+#else
+#include "sha1dc/sha1.h"
+#endif
+
+#ifdef DC_SHA1_EXTERNAL
+void git_SHA1DCInit(SHA1_CTX *);
+#else
+#define git_SHA1DCInit SHA1DCInit
+#endif
 
-/*
- * Same as SHA1DCUpdate, but adjust types to match git's usual interface.
- */
+void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *);
 void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, unsigned long len);
 
 #define platform_SHA_CTX SHA1_CTX
-#define platform_SHA1_Init SHA1DCInit
+#define platform_SHA1_Init git_SHA1DCInit
 #define platform_SHA1_Update git_SHA1DCUpdate
 #define platform_SHA1_Final git_SHA1DCFinal
index f5591e56dab67d6113deb8196c4a68c8af3f2ee0..1cc1c764151db233ba1cd43e06cbe87ba3f925ad 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -286,28 +286,26 @@ int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
        return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
 }
 
-static struct tempfile temporary_shallow;
-
 const char *setup_temporary_shallow(const struct oid_array *extra)
 {
+       struct tempfile *temp;
        struct strbuf sb = STRBUF_INIT;
-       int fd;
 
        if (write_shallow_commits(&sb, 0, extra)) {
-               fd = xmks_tempfile(&temporary_shallow, git_path("shallow_XXXXXX"));
+               temp = xmks_tempfile(git_path("shallow_XXXXXX"));
 
-               if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+               if (write_in_full(temp->fd, sb.buf, sb.len) != sb.len ||
+                   close_tempfile_gently(temp) < 0)
                        die_errno("failed to write to %s",
-                                 get_tempfile_path(&temporary_shallow));
-               close_tempfile(&temporary_shallow);
+                                 get_tempfile_path(temp));
                strbuf_release(&sb);
-               return get_tempfile_path(&temporary_shallow);
+               return get_tempfile_path(temp);
        }
        /*
         * is_repository_shallow() sees empty string as "no shallow
         * file".
         */
-       return get_tempfile_path(&temporary_shallow);
+       return "";
 }
 
 void setup_alternate_shallow(struct lock_file *shallow_lock,
index e705b94db55578aabb2063f7364220978cea5a40..7496cb8ec5a1f8baeda90f8653b2fcbbe2527390 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -147,7 +147,10 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len)
        if (len > (sb->alloc ? sb->alloc - 1 : 0))
                die("BUG: strbuf_setlen() beyond buffer");
        sb->len = len;
-       sb->buf[len] = '\0';
+       if (sb->buf != strbuf_slopbuf)
+               sb->buf[len] = '\0';
+       else
+               assert(!strbuf_slopbuf[0]);
 }
 
 /**
index 6edb97c1c68c063216bda710d5880b1d08de27da..6ccfaaba99c05c0e9112b142a2a0653dccda9eda 100644 (file)
@@ -184,8 +184,8 @@ static int handshake_capabilities(struct child_process *process,
                        if (supported_capabilities)
                                *supported_capabilities |= capabilities[i].flag;
                } else {
-                       warning("external filter requested unsupported filter capability '%s'",
-                               p);
+                       warning("subprocess '%s' requested unsupported capability '%s'",
+                               process->argv[0], p);
                }
        }
 
index 0c839019e1dca2e30ac0113303d55b4387bbe96d..2aa8a1747f8586839aa3036fbbc59f6c716c6128 100644 (file)
@@ -18,6 +18,7 @@ struct submodule_cache {
        struct hashmap for_path;
        struct hashmap for_name;
        unsigned initialized:1;
+       unsigned gitmodules_read:1;
 };
 
 /*
@@ -99,6 +100,7 @@ static void submodule_cache_clear(struct submodule_cache *cache)
        hashmap_free(&cache->for_path, 1);
        hashmap_free(&cache->for_name, 1);
        cache->initialized = 0;
+       cache->gitmodules_read = 0;
 }
 
 void submodule_cache_free(struct submodule_cache *cache)
@@ -455,9 +457,9 @@ static int parse_config(const char *var, const char *value, void *data)
        return ret;
 }
 
-int gitmodule_oid_from_commit(const struct object_id *treeish_name,
-                                     struct object_id *gitmodules_oid,
-                                     struct strbuf *rev)
+static int gitmodule_oid_from_commit(const struct object_id *treeish_name,
+                                    struct object_id *gitmodules_oid,
+                                    struct strbuf *rev)
 {
        int ret = 0;
 
@@ -558,13 +560,11 @@ static void submodule_cache_check_init(struct repository *repo)
        submodule_cache_init(repo->submodule_cache);
 }
 
-int submodule_config_option(struct repository *repo,
-                           const char *var, const char *value)
+static int gitmodules_cb(const char *var, const char *value, void *data)
 {
+       struct repository *repo = data;
        struct parse_config_parameter parameter;
 
-       submodule_cache_check_init(repo);
-
        parameter.cache = repo->submodule_cache;
        parameter.treeish_name = NULL;
        parameter.gitmodules_sha1 = null_sha1;
@@ -573,22 +573,63 @@ int submodule_config_option(struct repository *repo,
        return parse_config(var, value, &parameter);
 }
 
-int parse_submodule_config_option(const char *var, const char *value)
+void repo_read_gitmodules(struct repository *repo)
 {
-       return submodule_config_option(the_repository, var, value);
+       submodule_cache_check_init(repo);
+
+       if (repo->worktree) {
+               char *gitmodules;
+
+               if (repo_read_index(repo) < 0)
+                       return;
+
+               gitmodules = repo_worktree_path(repo, GITMODULES_FILE);
+
+               if (!is_gitmodules_unmerged(repo->index))
+                       git_config_from_file(gitmodules_cb, gitmodules, repo);
+
+               free(gitmodules);
+       }
+
+       repo->submodule_cache->gitmodules_read = 1;
+}
+
+void gitmodules_config_oid(const struct object_id *commit_oid)
+{
+       struct strbuf rev = STRBUF_INIT;
+       struct object_id oid;
+
+       submodule_cache_check_init(the_repository);
+
+       if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {
+               git_config_from_blob_oid(gitmodules_cb, rev.buf,
+                                        &oid, the_repository);
+       }
+       strbuf_release(&rev);
+
+       the_repository->submodule_cache->gitmodules_read = 1;
+}
+
+static void gitmodules_read_check(struct repository *repo)
+{
+       submodule_cache_check_init(repo);
+
+       /* read the repo's .gitmodules file if it hasn't been already */
+       if (!repo->submodule_cache->gitmodules_read)
+               repo_read_gitmodules(repo);
 }
 
 const struct submodule *submodule_from_name(const struct object_id *treeish_name,
                const char *name)
 {
-       submodule_cache_check_init(the_repository);
+       gitmodules_read_check(the_repository);
        return config_from(the_repository->submodule_cache, treeish_name, name, lookup_name);
 }
 
 const struct submodule *submodule_from_path(const struct object_id *treeish_name,
                const char *path)
 {
-       submodule_cache_check_init(the_repository);
+       gitmodules_read_check(the_repository);
        return config_from(the_repository->submodule_cache, treeish_name, path, lookup_path);
 }
 
@@ -596,7 +637,7 @@ const struct submodule *submodule_from_cache(struct repository *repo,
                                             const struct object_id *treeish_name,
                                             const char *key)
 {
-       submodule_cache_check_init(repo);
+       gitmodules_read_check(repo);
        return config_from(repo->submodule_cache, treeish_name,
                           key, lookup_path);
 }
index cccd34b929f5299fbba6f43c74daeb96773aebdf..e3845831f6cbd25097ea54b3e2246faa8a32e192 100644 (file)
@@ -34,9 +34,8 @@ 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 int parse_submodule_config_option(const char *var, const char *value);
-extern int submodule_config_option(struct repository *repo,
-                                  const char *var, const char *value);
+extern void repo_read_gitmodules(struct repository *repo);
+extern void gitmodules_config_oid(const struct object_id *commit_oid);
 extern const struct submodule *submodule_from_name(
                const struct object_id *commit_or_tree, const char *name);
 extern const struct submodule *submodule_from_path(
@@ -44,9 +43,6 @@ extern const struct submodule *submodule_from_path(
 extern const struct submodule *submodule_from_cache(struct repository *repo,
                                                    const struct object_id *treeish_name,
                                                    const char *key);
-extern int gitmodule_oid_from_commit(const struct object_id *commit_oid,
-                                    struct object_id *gitmodules_oid,
-                                    struct strbuf *rev);
 extern void submodule_free(void);
 
 #endif /* SUBMODULE_CONFIG_H */
index e072036e7965ca82a665ac1eeb88bfb44fa4569a..075c55f3ca8101e06731ef3b45e51d11ec00c7f6 100644 (file)
@@ -69,6 +69,13 @@ int is_staging_gitmodules_ok(const struct index_state *istate)
        return 1;
 }
 
+static int for_each_remote_ref_submodule(const char *submodule,
+                                        each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
+                                       fn, cb_data);
+}
+
 /*
  * Try to update the "path" entry in the "submodule.<name>" section of the
  * .gitmodules file. Return 0 only if a .gitmodules file was found, a section
@@ -165,31 +172,18 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
 {
        const struct submodule *submodule = submodule_from_path(&null_oid, path);
        if (submodule) {
-               if (submodule->ignore)
-                       handle_ignore_submodules_arg(diffopt, submodule->ignore);
-               else if (is_gitmodules_unmerged(&the_index))
-                       DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
-       }
-}
+               const char *ignore;
+               char *key;
 
-/* For loading from the .gitmodules file. */
-static int git_modules_config(const char *var, const char *value, void *cb)
-{
-       if (starts_with(var, "submodule."))
-               return parse_submodule_config_option(var, value);
-       return 0;
-}
+               key = xstrfmt("submodule.%s.ignore", submodule->name);
+               if (repo_config_get_string_const(the_repository, key, &ignore))
+                       ignore = submodule->ignore;
+               free(key);
 
-/* Loads all submodule settings from the config. */
-int submodule_config(const char *var, const char *value, void *cb)
-{
-       if (!strcmp(var, "submodule.recurse")) {
-               int v = git_config_bool(var, value) ?
-                       RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
-               config_update_recurse_submodules = v;
-               return 0;
-       } else {
-               return git_modules_config(var, value, cb);
+               if (ignore)
+                       handle_ignore_submodules_arg(diffopt, ignore);
+               else if (is_gitmodules_unmerged(&the_index))
+                       DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
        }
 }
 
@@ -221,55 +215,6 @@ int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
        return 0;
 }
 
-void load_submodule_cache(void)
-{
-       if (config_update_recurse_submodules == RECURSE_SUBMODULES_OFF)
-               return;
-
-       gitmodules_config();
-       git_config(submodule_config, NULL);
-}
-
-static int gitmodules_cb(const char *var, const char *value, void *data)
-{
-       struct repository *repo = data;
-       return submodule_config_option(repo, var, value);
-}
-
-void repo_read_gitmodules(struct repository *repo)
-{
-       if (repo->worktree) {
-               char *gitmodules;
-
-               if (repo_read_index(repo) < 0)
-                       return;
-
-               gitmodules = repo_worktree_path(repo, GITMODULES_FILE);
-
-               if (!is_gitmodules_unmerged(repo->index))
-                       git_config_from_file(gitmodules_cb, gitmodules, repo);
-
-               free(gitmodules);
-       }
-}
-
-void gitmodules_config(void)
-{
-       repo_read_gitmodules(the_repository);
-}
-
-void gitmodules_config_oid(const struct object_id *commit_oid)
-{
-       struct strbuf rev = STRBUF_INIT;
-       struct object_id oid;
-
-       if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {
-               git_config_from_blob_oid(submodule_config, rev.buf,
-                                        &oid, NULL);
-       }
-       strbuf_release(&rev);
-}
-
 /*
  * Determine if a submodule has been initialized at a given 'path'
  */
@@ -398,24 +343,38 @@ void die_path_inside_submodule(const struct index_state *istate,
        }
 }
 
-int parse_submodule_update_strategy(const char *value,
-               struct submodule_update_strategy *dst)
+enum submodule_update_type parse_submodule_update_type(const char *value)
 {
-       free((void*)dst->command);
-       dst->command = NULL;
        if (!strcmp(value, "none"))
-               dst->type = SM_UPDATE_NONE;
+               return SM_UPDATE_NONE;
        else if (!strcmp(value, "checkout"))
-               dst->type = SM_UPDATE_CHECKOUT;
+               return SM_UPDATE_CHECKOUT;
        else if (!strcmp(value, "rebase"))
-               dst->type = SM_UPDATE_REBASE;
+               return SM_UPDATE_REBASE;
        else if (!strcmp(value, "merge"))
-               dst->type = SM_UPDATE_MERGE;
-       else if (skip_prefix(value, "!", &value)) {
-               dst->type = SM_UPDATE_COMMAND;
-               dst->command = xstrdup(value);
-       } else
+               return SM_UPDATE_MERGE;
+       else if (*value == '!')
+               return SM_UPDATE_COMMAND;
+       else
+               return SM_UPDATE_UNSPECIFIED;
+}
+
+int parse_submodule_update_strategy(const char *value,
+               struct submodule_update_strategy *dst)
+{
+       enum submodule_update_type type;
+
+       free((void*)dst->command);
+       dst->command = NULL;
+
+       type = parse_submodule_update_type(value);
+       if (type == SM_UPDATE_UNSPECIFIED)
                return -1;
+
+       dst->type = type;
+       if (type == SM_UPDATE_COMMAND)
+               dst->command = xstrdup(value + 1);
+
        return 0;
 }
 
@@ -478,9 +437,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
        return prepare_revision_walk(rev);
 }
 
-static void print_submodule_summary(struct rev_info *rev, FILE *f,
-               const char *line_prefix,
-               const char *del, const char *add, const char *reset)
+static void print_submodule_summary(struct rev_info *rev, struct diff_options *o)
 {
        static const char format[] = "  %m %s";
        struct strbuf sb = STRBUF_INIT;
@@ -491,18 +448,12 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
                ctx.date_mode = rev->date_mode;
                ctx.output_encoding = get_log_output_encoding();
                strbuf_setlen(&sb, 0);
-               strbuf_addstr(&sb, line_prefix);
-               if (commit->object.flags & SYMMETRIC_LEFT) {
-                       if (del)
-                               strbuf_addstr(&sb, del);
-               }
-               else if (add)
-                       strbuf_addstr(&sb, add);
                format_commit_message(commit, format, &sb, &ctx);
-               if (reset)
-                       strbuf_addstr(&sb, reset);
                strbuf_addch(&sb, '\n');
-               fprintf(f, "%s", sb.buf);
+               if (commit->object.flags & SYMMETRIC_LEFT)
+                       diff_emit_submodule_del(o, sb.buf);
+               else
+                       diff_emit_submodule_add(o, sb.buf);
        }
        strbuf_release(&sb);
 }
@@ -529,11 +480,9 @@ void prepare_submodule_repo_env(struct argv_array *out)
  * attempt to lookup both the left and right commits and put them into the
  * left and right pointers.
  */
-static void show_submodule_header(FILE *f, const char *path,
-               const char *line_prefix,
+static void show_submodule_header(struct diff_options *o, const char *path,
                struct object_id *one, struct object_id *two,
-               unsigned dirty_submodule, const char *meta,
-               const char *reset,
+               unsigned dirty_submodule,
                struct commit **left, struct commit **right,
                struct commit_list **merge_bases)
 {
@@ -542,11 +491,10 @@ static void show_submodule_header(FILE *f, const char *path,
        int fast_forward = 0, fast_backward = 0;
 
        if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
-               fprintf(f, "%sSubmodule %s contains untracked content\n",
-                       line_prefix, path);
+               diff_emit_submodule_untracked(o, path);
+
        if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-               fprintf(f, "%sSubmodule %s contains modified content\n",
-                       line_prefix, path);
+               diff_emit_submodule_modified(o, path);
 
        if (is_null_oid(one))
                message = "(new submodule)";
@@ -588,31 +536,29 @@ static void show_submodule_header(FILE *f, const char *path,
        }
 
 output_header:
-       strbuf_addf(&sb, "%s%sSubmodule %s ", line_prefix, meta, path);
+       strbuf_addf(&sb, "Submodule %s ", path);
        strbuf_add_unique_abbrev(&sb, one->hash, DEFAULT_ABBREV);
        strbuf_addstr(&sb, (fast_backward || fast_forward) ? ".." : "...");
        strbuf_add_unique_abbrev(&sb, two->hash, DEFAULT_ABBREV);
        if (message)
-               strbuf_addf(&sb, " %s%s\n", message, reset);
+               strbuf_addf(&sb, " %s\n", message);
        else
-               strbuf_addf(&sb, "%s:%s\n", fast_backward ? " (rewind)" : "", reset);
-       fwrite(sb.buf, sb.len, 1, f);
+               strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : "");
+       diff_emit_submodule_header(o, sb.buf);
 
        strbuf_release(&sb);
 }
 
-void show_submodule_summary(FILE *f, const char *path,
-               const char *line_prefix,
+void show_submodule_summary(struct diff_options *o, const char *path,
                struct object_id *one, struct object_id *two,
-               unsigned dirty_submodule, const char *meta,
-               const char *del, const char *add, const char *reset)
+               unsigned dirty_submodule)
 {
        struct rev_info rev;
        struct commit *left = NULL, *right = NULL;
        struct commit_list *merge_bases = NULL;
 
-       show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
-                             meta, reset, &left, &right, &merge_bases);
+       show_submodule_header(o, path, one, two, dirty_submodule,
+                             &left, &right, &merge_bases);
 
        /*
         * If we don't have both a left and a right pointer, there is no
@@ -624,11 +570,11 @@ void show_submodule_summary(FILE *f, const char *path,
 
        /* Treat revision walker failure the same as missing commits */
        if (prepare_submodule_summary(&rev, path, left, right, merge_bases)) {
-               fprintf(f, "%s(revision walker failed)\n", line_prefix);
+               diff_emit_submodule_error(o, "(revision walker failed)\n");
                goto out;
        }
 
-       print_submodule_summary(&rev, f, line_prefix, del, add, reset);
+       print_submodule_summary(&rev, o);
 
 out:
        if (merge_bases)
@@ -637,21 +583,18 @@ void show_submodule_summary(FILE *f, const char *path,
        clear_commit_marks(right, ~0);
 }
 
-void show_submodule_inline_diff(FILE *f, const char *path,
-               const char *line_prefix,
+void show_submodule_inline_diff(struct diff_options *o, const char *path,
                struct object_id *one, struct object_id *two,
-               unsigned dirty_submodule, const char *meta,
-               const char *del, const char *add, const char *reset,
-               const struct diff_options *o)
+               unsigned dirty_submodule)
 {
        const struct object_id *old = &empty_tree_oid, *new = &empty_tree_oid;
        struct commit *left = NULL, *right = NULL;
        struct commit_list *merge_bases = NULL;
-       struct strbuf submodule_dir = STRBUF_INIT;
        struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf sb = STRBUF_INIT;
 
-       show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
-                             meta, reset, &left, &right, &merge_bases);
+       show_submodule_header(o, path, one, two, dirty_submodule,
+                             &left, &right, &merge_bases);
 
        /* We need a valid left and right commit to display a difference */
        if (!(left || is_null_oid(one)) ||
@@ -663,16 +606,16 @@ void show_submodule_inline_diff(FILE *f, const char *path,
        if (right)
                new = two;
 
-       fflush(f);
        cp.git_cmd = 1;
        cp.dir = path;
-       cp.out = dup(fileno(f));
+       cp.out = -1;
        cp.no_stdin = 1;
 
        /* TODO: other options may need to be passed here. */
        argv_array_pushl(&cp.args, "diff", "--submodule=diff", NULL);
+       argv_array_pushf(&cp.args, "--color=%s", want_color(o->use_color) ?
+                        "always" : "never");
 
-       argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix);
        if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
                argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
                                 o->b_prefix, path);
@@ -695,11 +638,17 @@ void show_submodule_inline_diff(FILE *f, const char *path,
                argv_array_push(&cp.args, oid_to_hex(new));
 
        prepare_submodule_repo_env(&cp.env_array);
-       if (run_command(&cp))
-               fprintf(f, "(diff failed)\n");
+       if (start_command(&cp))
+               diff_emit_submodule_error(o, "(diff failed)\n");
+
+       while (strbuf_getwholeline_fd(&sb, cp.out, '\n') != EOF)
+               diff_emit_submodule_pipethrough(o, sb.buf, sb.len);
+
+       if (finish_command(&cp))
+               diff_emit_submodule_error(o, "(diff failed)\n");
 
 done:
-       strbuf_release(&submodule_dir);
+       strbuf_release(&sb);
        if (merge_bases)
                free_commit_list(merge_bases);
        if (left)
@@ -1140,7 +1089,6 @@ int submodule_touches_in_range(struct object_id *excl_oid,
        struct argv_array args = ARGV_ARRAY_INIT;
        int ret;
 
-       gitmodules_config();
        /* No need to check if there are no submodules configured */
        if (!submodule_from_path(NULL, NULL))
                return 0;
@@ -1189,19 +1137,27 @@ static int get_next_submodule(struct child_process *cp,
                        continue;
 
                submodule = submodule_from_path(&null_oid, ce->name);
-               if (!submodule)
-                       submodule = submodule_from_name(&null_oid, ce->name);
 
                default_argv = "yes";
                if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
-                       if (submodule &&
-                           submodule->fetch_recurse !=
-                                               RECURSE_SUBMODULES_NONE) {
-                               if (submodule->fetch_recurse ==
-                                               RECURSE_SUBMODULES_OFF)
+                       int fetch_recurse = RECURSE_SUBMODULES_NONE;
+
+                       if (submodule) {
+                               char *key;
+                               const char *value;
+
+                               fetch_recurse = submodule->fetch_recurse;
+                               key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
+                               if (!repo_config_get_string_const(the_repository, key, &value)) {
+                                       fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
+                               }
+                               free(key);
+                       }
+
+                       if (fetch_recurse != RECURSE_SUBMODULES_NONE) {
+                               if (fetch_recurse == RECURSE_SUBMODULES_OFF)
                                        continue;
-                               if (submodule->fetch_recurse ==
-                                               RECURSE_SUBMODULES_ON_DEMAND) {
+                               if (fetch_recurse == RECURSE_SUBMODULES_ON_DEMAND) {
                                        if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
                                                continue;
                                        default_argv = "on-demand";
@@ -1678,6 +1634,8 @@ static int find_first_merges(struct object_array *result, const char *path,
                        oid_to_hex(&a->object.oid));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
+       /* FIXME: can't handle linked worktrees in submodules yet */
+       revs.single_worktree = path != NULL;
        setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
 
        /* save all revisions from the above list that contain b */
@@ -2039,7 +1997,6 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
                strbuf_addstr(buf, git_dir);
        }
        if (!is_git_directory(buf->buf)) {
-               gitmodules_config();
                sub = submodule_from_path(&null_oid, submodule);
                if (!sub) {
                        ret = -1;
index e402b004ff0a7d983d196eb1fc0e34bdd2bacb93..6b52133c88b2306e77fe6f759507b38c0ec0d6ce 100644 (file)
@@ -40,16 +40,11 @@ extern int remove_path_from_gitmodules(const char *path);
 extern void stage_updated_gitmodules(void);
 extern void set_diffopt_flags_from_submodule_config(struct diff_options *,
                const char *path);
-extern int submodule_config(const char *var, const char *value, void *cb);
 extern int git_default_submodule_config(const char *var, const char *value, void *cb);
 
 struct option;
 int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
                                                     const char *arg, int unset);
-void load_submodule_cache(void);
-extern void gitmodules_config(void);
-extern void repo_read_gitmodules(struct repository *repo);
-extern void gitmodules_config_oid(const struct object_id *commit_oid);
 extern int is_submodule_active(struct repository *repo, const char *path);
 /*
  * Determine if a submodule has been populated at a given 'path' by checking if
@@ -62,21 +57,17 @@ extern void die_in_unpopulated_submodule(const struct index_state *istate,
                                         const char *prefix);
 extern void die_path_inside_submodule(const struct index_state *istate,
                                      const struct pathspec *ps);
+extern enum submodule_update_type parse_submodule_update_type(const char *value);
 extern int parse_submodule_update_strategy(const char *value,
                struct submodule_update_strategy *dst);
 extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
 extern void handle_ignore_submodules_arg(struct diff_options *, const char *);
-extern void show_submodule_summary(FILE *f, const char *path,
-               const char *line_prefix,
+extern void show_submodule_summary(struct diff_options *o, const char *path,
                struct object_id *one, struct object_id *two,
-               unsigned dirty_submodule, const char *meta,
-               const char *del, const char *add, const char *reset);
-extern void show_submodule_inline_diff(FILE *f, const char *path,
-               const char *line_prefix,
+               unsigned dirty_submodule);
+extern void show_submodule_inline_diff(struct diff_options *o, const char *path,
                struct object_id *one, struct object_id *two,
-               unsigned dirty_submodule, const char *meta,
-               const char *del, const char *add, const char *reset,
-               const struct diff_options *opt);
+               unsigned dirty_submodule);
 /* Check if we want to update any submodule.*/
 extern int should_update_submodules(void);
 /*
index e13fbcc1b5c271c1b2a4a2b3449544e844282358..f23db3b19a9911b554ca8eaf567cd0370d42af6e 100644 (file)
@@ -10,11 +10,6 @@ static void die_usage(int argc, const char **argv, const char *msg)
        exit(1);
 }
 
-static int git_test_config(const char *var, const char *value, void *cb)
-{
-       return parse_submodule_config_option(var, value);
-}
-
 int cmd_main(int argc, const char **argv)
 {
        const char **arg = argv;
@@ -37,8 +32,6 @@ int cmd_main(int argc, const char **argv)
                die_usage(argc, argv, "Wrong number of arguments.");
 
        setup_git_directory();
-       gitmodules_config();
-       git_config(git_test_config, NULL);
 
        while (*arg) {
                struct object_id commit_oid;
diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c
new file mode 100644 (file)
index 0000000..b7ee039
--- /dev/null
@@ -0,0 +1,23 @@
+#include "cache.h"
+#include "lockfile.h"
+
+static struct lock_file index_lock;
+
+int cmd_main(int argc, const char **argv)
+{
+       int i, cnt = 1, lockfd;
+       if (argc == 2)
+               cnt = strtol(argv[1], NULL, 0);
+       setup_git_directory();
+       read_cache();
+       for (i = 0; i < cnt; i++) {
+               lockfd = hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+               if (0 <= lockfd) {
+                       write_locked_index(&the_index, &index_lock, COMMIT_LOCK);
+               } else {
+                       rollback_lock_file(&index_lock);
+               }
+       }
+
+       return 0;
+}
diff --git a/t/perf/p0007-write-cache.sh b/t/perf/p0007-write-cache.sh
new file mode 100755 (executable)
index 0000000..261fe92
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description="Tests performance of writing the index"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_expect_success "setup repo" '
+       if git rev-parse --verify refs/heads/p0006-ballast^{commit}
+       then
+               echo Assuming synthetic repo from many-files.sh
+               git config --local core.sparsecheckout 1
+               cat >.git/info/sparse-checkout <<-EOF
+               /*
+               !ballast/*
+               EOF
+       else
+               echo Assuming non-synthetic repo...
+       fi &&
+       nr_files=$(git ls-files | wc -l)
+'
+
+count=3
+test_perf "write_locked_index $count times ($nr_files files)" "
+       test-write-cache $count
+"
+
+test_done
index 5df06f355641ad7a38daf34f9fc841e7fe4f8a0f..8842d0329fb7947e811d4ffccff3bba9e8885d5d 100755 (executable)
@@ -49,4 +49,34 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' '
        test_cmp expected actual
 '
 
+test_expect_success 'for_each_reflog()' '
+       echo $_z40 > .git/logs/PSEUDO-MAIN &&
+       mkdir -p     .git/logs/refs/bisect &&
+       echo $_z40 > .git/logs/refs/bisect/random &&
+
+       echo $_z40 > .git/worktrees/wt/logs/PSEUDO-WT &&
+       mkdir -p     .git/worktrees/wt/logs/refs/bisect &&
+       echo $_z40 > .git/worktrees/wt/logs/refs/bisect/wt-random &&
+
+       $RWT for-each-reflog | cut -c 42- | sort >actual &&
+       cat >expected <<-\EOF &&
+       HEAD 0x1
+       PSEUDO-WT 0x0
+       refs/bisect/wt-random 0x0
+       refs/heads/master 0x0
+       refs/heads/wt-master 0x0
+       EOF
+       test_cmp expected actual &&
+
+       $RMAIN for-each-reflog | cut -c 42- | sort >actual &&
+       cat >expected <<-\EOF &&
+       HEAD 0x1
+       PSEUDO-MAIN 0x0
+       refs/bisect/random 0x0
+       refs/heads/master 0x0
+       refs/heads/wt-master 0x0
+       EOF
+       test_cmp expected actual
+'
+
 test_done
index 9d707d2a40a457abcd85fb93d9f8195946d09d01..d971649979fa2661923d9df797125cd4d997cf97 100755 (executable)
@@ -162,6 +162,19 @@ test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD'
        grep "^0\{40\}.*$msg$" .git/logs/HEAD
 '
 
+test_expect_success 'git branch -M should leave orphaned HEAD alone' '
+       git init orphan &&
+       (
+               cd orphan &&
+               test_commit initial &&
+               git checkout --orphan lonely &&
+               grep lonely .git/HEAD &&
+               test_path_is_missing .git/refs/head/lonely &&
+               git branch -M master mistress &&
+               grep lonely .git/HEAD
+       )
+'
+
 test_expect_success 'resulting reflog can be shown by log -g' '
        oid=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
@@ -559,6 +572,7 @@ test_expect_success 'use --set-upstream-to modify HEAD' '
 test_expect_success 'use --set-upstream-to modify a particular branch' '
        git branch my13 &&
        git branch --set-upstream-to master my13 &&
+       test_when_finished "git branch --unset-upstream my13" &&
        test "$(git config branch.my13.remote)" = "." &&
        test "$(git config branch.my13.merge)" = "refs/heads/master"
 '
@@ -604,38 +618,8 @@ test_expect_success 'test --unset-upstream on a particular branch' '
        test_must_fail git config branch.my14.merge
 '
 
-test_expect_success '--set-upstream shows message when creating a new branch that exists as remote-tracking' '
-       git update-ref refs/remotes/origin/master HEAD &&
-       git branch --set-upstream origin/master 2>actual &&
-       test_when_finished git update-ref -d refs/remotes/origin/master &&
-       test_when_finished git branch -d origin/master &&
-       cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-
-If you wanted to make '"'master'"' track '"'origin/master'"', do this:
-
-    git branch -d origin/master
-    git branch --set-upstream-to origin/master
-EOF
-       test_i18ncmp expected actual
-'
-
-test_expect_success '--set-upstream with two args only shows the deprecation message' '
-       git branch --set-upstream master my13 2>actual &&
-       test_when_finished git branch --unset-upstream master &&
-       cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-EOF
-       test_i18ncmp expected actual
-'
-
-test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' '
-       git branch --set-upstream my13 2>actual &&
-       test_when_finished git branch --unset-upstream my13 &&
-       cat >expected <<EOF &&
-The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
-EOF
-       test_i18ncmp expected actual
+test_expect_success '--set-upstream fails' '
+    test_must_fail git branch --set-upstream origin/master
 '
 
 test_expect_success '--set-upstream-to notices an error to set branch as own upstream' '
@@ -960,19 +944,6 @@ test_expect_success 'attempt to delete a branch merged to its base' '
        test_must_fail git branch -d my10
 '
 
-test_expect_success 'use set-upstream on the current branch' '
-       git checkout master &&
-       git --bare init myupstream.git &&
-       git push myupstream.git master:refs/heads/frotz &&
-       git remote add origin myupstream.git &&
-       git fetch &&
-       git branch --set-upstream master origin/frotz &&
-
-       test "z$(git config branch.master.remote)" = "zorigin" &&
-       test "z$(git config branch.master.merge)" = "zrefs/heads/frotz"
-
-'
-
 test_expect_success 'use --edit-description' '
        write_script editor <<-\EOF &&
                echo "New contents" >"$1"
index 4046817d70a0ac1dd5b11a49c500896688a4f0b1..3b1ac1971af1d972b77b3dfe8ae4a56e00e1fa55 100755 (executable)
@@ -444,6 +444,14 @@ test_expect_failure 'stash file to directory' '
        test foo = "$(cat file/file)"
 '
 
+test_expect_success 'stash create - no changes' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD" &&
+       git reset --hard &&
+       git stash create >actual &&
+       test_must_be_empty actual
+'
+
 test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD" &&
@@ -648,6 +656,20 @@ test_expect_success 'stash branch should not drop the stash if the branch exists
        git rev-parse stash@{0} --
 '
 
+test_expect_success 'stash branch should not drop the stash if the apply fails' '
+       git stash clear &&
+       git reset HEAD~1 --hard &&
+       echo foo >file &&
+       git add file &&
+       git commit -m initial &&
+       echo bar >file &&
+       git stash &&
+       echo baz >file &&
+       test_when_finished "git checkout master" &&
+       test_must_fail git stash branch new_branch stash@{0} &&
+       git rev-parse stash@{0} --
+'
+
 test_expect_success 'stash apply shows status same as git status (relative to current directory)' '
        git stash clear &&
        echo 1 >subdir/subfile1 &&
@@ -800,6 +822,18 @@ test_expect_success 'create with multiple arguments for the message' '
        test_cmp expect actual
 '
 
+test_expect_success 'create in a detached state' '
+       test_when_finished "git checkout master" &&
+       git checkout HEAD~1 &&
+       >foo &&
+       git add foo &&
+       STASH_ID=$(git stash create) &&
+       HEAD_ID=$(git rev-parse --short HEAD) &&
+       echo "WIP on (no branch): ${HEAD_ID} initial" >expect &&
+       git show --pretty=%s -s ${STASH_ID} >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'stash -- <pathspec> stashes and restores the file' '
        >foo &&
        >bar &&
index 289806d0c7eb02e0acc5244df5e3999d45b4e085..12d182dc1bba82401a929e6207eb2dcc7ea21672 100755 (executable)
@@ -972,4 +972,563 @@ test_expect_success 'option overrides diff.wsErrorHighlight' '
 
 '
 
+test_expect_success 'detect moved code, complete file' '
+       git reset --hard &&
+       cat <<-\EOF >test.c &&
+       #include<stdio.h>
+       main()
+       {
+       printf("Hello World");
+       }
+       EOF
+       git add test.c &&
+       git commit -m "add main function" &&
+       git mv test.c main.c &&
+       test_config color.diff.oldMoved "normal red" &&
+       test_config color.diff.newMoved "normal green" &&
+       git diff HEAD --color-moved=zebra --no-renames | test_decode_color >actual &&
+       cat >expected <<-\EOF &&
+       <BOLD>diff --git a/main.c b/main.c<RESET>
+       <BOLD>new file mode 100644<RESET>
+       <BOLD>index 0000000..a986c57<RESET>
+       <BOLD>--- /dev/null<RESET>
+       <BOLD>+++ b/main.c<RESET>
+       <CYAN>@@ -0,0 +1,5 @@<RESET>
+       <BGREEN>+<RESET><BGREEN>#include<stdio.h><RESET>
+       <BGREEN>+<RESET><BGREEN>main()<RESET>
+       <BGREEN>+<RESET><BGREEN>{<RESET>
+       <BGREEN>+<RESET><BGREEN>printf("Hello World");<RESET>
+       <BGREEN>+<RESET><BGREEN>}<RESET>
+       <BOLD>diff --git a/test.c b/test.c<RESET>
+       <BOLD>deleted file mode 100644<RESET>
+       <BOLD>index a986c57..0000000<RESET>
+       <BOLD>--- a/test.c<RESET>
+       <BOLD>+++ /dev/null<RESET>
+       <CYAN>@@ -1,5 +0,0 @@<RESET>
+       <BRED>-#include<stdio.h><RESET>
+       <BRED>-main()<RESET>
+       <BRED>-{<RESET>
+       <BRED>-printf("Hello World");<RESET>
+       <BRED>-}<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success 'detect malicious moved code, inside file' '
+       test_config color.diff.oldMoved "normal red" &&
+       test_config color.diff.newMoved "normal green" &&
+       test_config color.diff.oldMovedAlternative "blue" &&
+       test_config color.diff.newMovedAlternative "yellow" &&
+       git reset --hard &&
+       cat <<-\EOF >main.c &&
+               #include<stdio.h>
+               int stuff()
+               {
+                       printf("Hello ");
+                       printf("World\n");
+               }
+
+               int secure_foo(struct user *u)
+               {
+                       if (!u->is_allowed_foo)
+                               return;
+                       foo(u);
+               }
+
+               int main()
+               {
+                       foo();
+               }
+       EOF
+       cat <<-\EOF >test.c &&
+               #include<stdio.h>
+               int bar()
+               {
+                       printf("Hello World, but different\n");
+               }
+
+               int another_function()
+               {
+                       bar();
+               }
+       EOF
+       git add main.c test.c &&
+       git commit -m "add main and test file" &&
+       cat <<-\EOF >main.c &&
+               #include<stdio.h>
+               int stuff()
+               {
+                       printf("Hello ");
+                       printf("World\n");
+               }
+
+               int main()
+               {
+                       foo();
+               }
+       EOF
+       cat <<-\EOF >test.c &&
+               #include<stdio.h>
+               int bar()
+               {
+                       printf("Hello World, but different\n");
+               }
+
+               int secure_foo(struct user *u)
+               {
+                       foo(u);
+                       if (!u->is_allowed_foo)
+                               return;
+               }
+
+               int another_function()
+               {
+                       bar();
+               }
+       EOF
+       git diff HEAD --no-renames --color-moved=zebra| test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/main.c b/main.c<RESET>
+       <BOLD>index 27a619c..7cf9336 100644<RESET>
+       <BOLD>--- a/main.c<RESET>
+       <BOLD>+++ b/main.c<RESET>
+       <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET>
+        printf("World\n");<RESET>
+        }<RESET>
+        <RESET>
+       <BRED>-int secure_foo(struct user *u)<RESET>
+       <BRED>-{<RESET>
+       <BLUE>-if (!u->is_allowed_foo)<RESET>
+       <BLUE>-return;<RESET>
+       <RED>-foo(u);<RESET>
+       <RED>-}<RESET>
+       <RED>-<RESET>
+        int main()<RESET>
+        {<RESET>
+        foo();<RESET>
+       <BOLD>diff --git a/test.c b/test.c<RESET>
+       <BOLD>index 1dc1d85..2bedec9 100644<RESET>
+       <BOLD>--- a/test.c<RESET>
+       <BOLD>+++ b/test.c<RESET>
+       <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET>
+        printf("Hello World, but different\n");<RESET>
+        }<RESET>
+        <RESET>
+       <BGREEN>+<RESET><BGREEN>int secure_foo(struct user *u)<RESET>
+       <BGREEN>+<RESET><BGREEN>{<RESET>
+       <GREEN>+<RESET><GREEN>foo(u);<RESET>
+       <BGREEN>+<RESET><BGREEN>if (!u->is_allowed_foo)<RESET>
+       <BGREEN>+<RESET><BGREEN>return;<RESET>
+       <GREEN>+<RESET><GREEN>}<RESET>
+       <GREEN>+<RESET>
+        int another_function()<RESET>
+        {<RESET>
+        bar();<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success 'plain moved code, inside file' '
+       test_config color.diff.oldMoved "normal red" &&
+       test_config color.diff.newMoved "normal green" &&
+       test_config color.diff.oldMovedAlternative "blue" &&
+       test_config color.diff.newMovedAlternative "yellow" &&
+       # needs previous test as setup
+       git diff HEAD --no-renames --color-moved=plain| test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/main.c b/main.c<RESET>
+       <BOLD>index 27a619c..7cf9336 100644<RESET>
+       <BOLD>--- a/main.c<RESET>
+       <BOLD>+++ b/main.c<RESET>
+       <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET>
+        printf("World\n");<RESET>
+        }<RESET>
+        <RESET>
+       <BRED>-int secure_foo(struct user *u)<RESET>
+       <BRED>-{<RESET>
+       <BRED>-if (!u->is_allowed_foo)<RESET>
+       <BRED>-return;<RESET>
+       <BRED>-foo(u);<RESET>
+       <BRED>-}<RESET>
+       <BRED>-<RESET>
+        int main()<RESET>
+        {<RESET>
+        foo();<RESET>
+       <BOLD>diff --git a/test.c b/test.c<RESET>
+       <BOLD>index 1dc1d85..2bedec9 100644<RESET>
+       <BOLD>--- a/test.c<RESET>
+       <BOLD>+++ b/test.c<RESET>
+       <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET>
+        printf("Hello World, but different\n");<RESET>
+        }<RESET>
+        <RESET>
+       <BGREEN>+<RESET><BGREEN>int secure_foo(struct user *u)<RESET>
+       <BGREEN>+<RESET><BGREEN>{<RESET>
+       <BGREEN>+<RESET><BGREEN>foo(u);<RESET>
+       <BGREEN>+<RESET><BGREEN>if (!u->is_allowed_foo)<RESET>
+       <BGREEN>+<RESET><BGREEN>return;<RESET>
+       <BGREEN>+<RESET><BGREEN>}<RESET>
+       <BGREEN>+<RESET>
+        int another_function()<RESET>
+        {<RESET>
+        bar();<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
+       git reset --hard &&
+       cat <<-\EOF >lines.txt &&
+               long line 1
+               long line 2
+               long line 3
+               line 4
+               line 5
+               line 6
+               line 7
+               line 8
+               line 9
+               line 10
+               line 11
+               line 12
+               line 13
+               long line 14
+               long line 15
+               long line 16
+       EOF
+       git add lines.txt &&
+       git commit -m "add poetry" &&
+       cat <<-\EOF >lines.txt &&
+               line 4
+               line 5
+               line 6
+               line 7
+               line 8
+               line 9
+               long line 1
+               long line 2
+               long line 3
+               long line 14
+               long line 15
+               long line 16
+               line 10
+               line 11
+               line 12
+               line 13
+       EOF
+       test_config color.diff.oldMoved "magenta" &&
+       test_config color.diff.newMoved "cyan" &&
+       test_config color.diff.oldMovedAlternative "blue" &&
+       test_config color.diff.newMovedAlternative "yellow" &&
+       test_config color.diff.oldMovedDimmed "normal magenta" &&
+       test_config color.diff.newMovedDimmed "normal cyan" &&
+       test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
+       test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
+       git diff HEAD --no-renames --color-moved=dimmed_zebra |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,16 +1,16 @@<RESET>
+       <BMAGENTA>-long line 1<RESET>
+       <BMAGENTA>-long line 2<RESET>
+       <BMAGENTA>-long line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+        line 6<RESET>
+        line 7<RESET>
+        line 8<RESET>
+        line 9<RESET>
+       <BCYAN>+<RESET><BCYAN>long line 1<RESET>
+       <BCYAN>+<RESET><BCYAN>long line 2<RESET>
+       <CYAN>+<RESET><CYAN>long line 3<RESET>
+       <YELLOW>+<RESET><YELLOW>long line 14<RESET>
+       <BYELLOW>+<RESET><BYELLOW>long line 15<RESET>
+       <BYELLOW>+<RESET><BYELLOW>long line 16<RESET>
+        line 10<RESET>
+        line 11<RESET>
+        line 12<RESET>
+        line 13<RESET>
+       <BMAGENTA>-long line 14<RESET>
+       <BMAGENTA>-long line 15<RESET>
+       <BMAGENTA>-long line 16<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'cmd option assumes configured colored-moved' '
+       test_config color.diff.oldMoved "magenta" &&
+       test_config color.diff.newMoved "cyan" &&
+       test_config color.diff.oldMovedAlternative "blue" &&
+       test_config color.diff.newMovedAlternative "yellow" &&
+       test_config color.diff.oldMovedDimmed "normal magenta" &&
+       test_config color.diff.newMovedDimmed "normal cyan" &&
+       test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
+       test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
+       test_config diff.colorMoved zebra &&
+       git diff HEAD --no-renames --color-moved |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,16 +1,16 @@<RESET>
+       <MAGENTA>-long line 1<RESET>
+       <MAGENTA>-long line 2<RESET>
+       <MAGENTA>-long line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+        line 6<RESET>
+        line 7<RESET>
+        line 8<RESET>
+        line 9<RESET>
+       <CYAN>+<RESET><CYAN>long line 1<RESET>
+       <CYAN>+<RESET><CYAN>long line 2<RESET>
+       <CYAN>+<RESET><CYAN>long line 3<RESET>
+       <YELLOW>+<RESET><YELLOW>long line 14<RESET>
+       <YELLOW>+<RESET><YELLOW>long line 15<RESET>
+       <YELLOW>+<RESET><YELLOW>long line 16<RESET>
+        line 10<RESET>
+        line 11<RESET>
+        line 12<RESET>
+        line 13<RESET>
+       <MAGENTA>-long line 14<RESET>
+       <MAGENTA>-long line 15<RESET>
+       <MAGENTA>-long line 16<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'no effect from --color-moved with --word-diff' '
+       cat <<-\EOF >text.txt &&
+       Lorem Ipsum is simply dummy text of the printing and typesetting industry.
+       EOF
+       git add text.txt &&
+       git commit -a -m "clean state" &&
+       cat <<-\EOF >text.txt &&
+       simply Lorem Ipsum dummy is text of the typesetting and printing industry.
+       EOF
+       git diff --color-moved --word-diff >actual &&
+       git diff --word-diff >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'move detection ignoring whitespace ' '
+       git reset --hard &&
+       cat <<\EOF >lines.txt &&
+line 1
+line 2
+line 3
+line 4
+long line 5
+long line 6
+long line 7
+EOF
+       git add lines.txt &&
+       git commit -m "add poetry" &&
+       cat <<\EOF >lines.txt &&
+       long line 5
+       long line 6
+       long line 7
+line 1
+line 2
+line 3
+line 4
+EOF
+       test_config color.diff.oldMoved "magenta" &&
+       test_config color.diff.newMoved "cyan" &&
+       git diff HEAD --no-renames --color-moved |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,7 +1,7 @@<RESET>
+       <GREEN>+<RESET> <GREEN>long line 5<RESET>
+       <GREEN>+<RESET> <GREEN>long line 6<RESET>
+       <GREEN>+<RESET> <GREEN>long line 7<RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+       <RED>-long line 5<RESET>
+       <RED>-long line 6<RESET>
+       <RED>-long line 7<RESET>
+       EOF
+       test_cmp expected actual &&
+
+       git diff HEAD --no-renames -w --color-moved |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,7 +1,7 @@<RESET>
+       <CYAN>+<RESET>  <CYAN>long line 5<RESET>
+       <CYAN>+<RESET>  <CYAN>long line 6<RESET>
+       <CYAN>+<RESET>  <CYAN>long line 7<RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+       <MAGENTA>-long line 5<RESET>
+       <MAGENTA>-long line 6<RESET>
+       <MAGENTA>-long line 7<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success '--color-moved block at end of diff output respects MIN_ALNUM_COUNT' '
+       git reset --hard &&
+       >bar &&
+       cat <<-\EOF >foo &&
+       irrelevant_line
+       line1
+       EOF
+       git add foo bar &&
+       git commit -m x &&
+
+       cat <<-\EOF >bar &&
+       line1
+       EOF
+       cat <<-\EOF >foo &&
+       irrelevant_line
+       EOF
+
+       git diff HEAD --color-moved=zebra --no-renames |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat >expected <<-\EOF &&
+       <BOLD>diff --git a/bar b/bar<RESET>
+       <BOLD>--- a/bar<RESET>
+       <BOLD>+++ b/bar<RESET>
+       <CYAN>@@ -0,0 +1 @@<RESET>
+       <GREEN>+<RESET><GREEN>line1<RESET>
+       <BOLD>diff --git a/foo b/foo<RESET>
+       <BOLD>--- a/foo<RESET>
+       <BOLD>+++ b/foo<RESET>
+       <CYAN>@@ -1,2 +1 @@<RESET>
+        irrelevant_line<RESET>
+       <RED>-line1<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success '--color-moved respects MIN_ALNUM_COUNT' '
+       git reset --hard &&
+       cat <<-\EOF >foo &&
+       nineteen chars 456789
+       irrelevant_line
+       twenty chars 234567890
+       EOF
+       >bar &&
+       git add foo bar &&
+       git commit -m x &&
+
+       cat <<-\EOF >foo &&
+       irrelevant_line
+       EOF
+       cat <<-\EOF >bar &&
+       twenty chars 234567890
+       nineteen chars 456789
+       EOF
+
+       git diff HEAD --color-moved=zebra --no-renames |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat >expected <<-\EOF &&
+       <BOLD>diff --git a/bar b/bar<RESET>
+       <BOLD>--- a/bar<RESET>
+       <BOLD>+++ b/bar<RESET>
+       <CYAN>@@ -0,0 +1,2 @@<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>twenty chars 234567890<RESET>
+       <GREEN>+<RESET><GREEN>nineteen chars 456789<RESET>
+       <BOLD>diff --git a/foo b/foo<RESET>
+       <BOLD>--- a/foo<RESET>
+       <BOLD>+++ b/foo<RESET>
+       <CYAN>@@ -1,3 +1 @@<RESET>
+       <RED>-nineteen chars 456789<RESET>
+        irrelevant_line<RESET>
+       <BOLD;MAGENTA>-twenty chars 234567890<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success '--color-moved treats adjacent blocks as separate for MIN_ALNUM_COUNT' '
+       git reset --hard &&
+       cat <<-\EOF >foo &&
+       7charsA
+       irrelevant_line
+       7charsB
+       7charsC
+       EOF
+       >bar &&
+       git add foo bar &&
+       git commit -m x &&
+
+       cat <<-\EOF >foo &&
+       irrelevant_line
+       EOF
+       cat <<-\EOF >bar &&
+       7charsB
+       7charsC
+       7charsA
+       EOF
+
+       git diff HEAD --color-moved=zebra --no-renames | grep -v "index" | test_decode_color >actual &&
+       cat >expected <<-\EOF &&
+       <BOLD>diff --git a/bar b/bar<RESET>
+       <BOLD>--- a/bar<RESET>
+       <BOLD>+++ b/bar<RESET>
+       <CYAN>@@ -0,0 +1,3 @@<RESET>
+       <GREEN>+<RESET><GREEN>7charsB<RESET>
+       <GREEN>+<RESET><GREEN>7charsC<RESET>
+       <GREEN>+<RESET><GREEN>7charsA<RESET>
+       <BOLD>diff --git a/foo b/foo<RESET>
+       <BOLD>--- a/foo<RESET>
+       <BOLD>+++ b/foo<RESET>
+       <CYAN>@@ -1,4 +1 @@<RESET>
+       <RED>-7charsA<RESET>
+        irrelevant_line<RESET>
+       <RED>-7charsB<RESET>
+       <RED>-7charsC<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success 'move detection with submodules' '
+       test_create_repo bananas &&
+       echo ripe >bananas/recipe &&
+       git -C bananas add recipe &&
+       test_commit fruit &&
+       test_commit -C bananas recipe &&
+       git submodule add ./bananas &&
+       git add bananas &&
+       git commit -a -m "bananas are like a heavy library?" &&
+       echo foul >bananas/recipe &&
+       echo ripe >fruit.t &&
+
+       git diff --submodule=diff --color-moved >actual &&
+
+       # no move detection as the moved line is across repository boundaries.
+       test_decode_color <actual >decoded_actual &&
+       ! grep BGREEN decoded_actual &&
+       ! grep BRED decoded_actual &&
+
+       # nor did we mess with it another way
+       git diff --submodule=diff | test_decode_color >expect &&
+       test_cmp expect decoded_actual
+'
+
 test_done
index 518bf9524e0b55181f3c440d202b69c12a0b314f..2ffd11a142d8c1d2f532f662951a27a90119a76e 100755 (executable)
@@ -113,35 +113,6 @@ test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)'
        ! test -s actual4
 '
 
-test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.git/config]' '
-       git config diff.ignoreSubmodules all &&
-       git diff HEAD >actual &&
-       ! test -s actual &&
-       git config submodule.subname.ignore none &&
-       git config submodule.subname.path sub &&
-       git diff HEAD >actual &&
-       sed -e "1,/^@@/d" actual >actual.body &&
-       expect_from_to >expect.body $subprev $subprev-dirty &&
-       test_cmp expect.body actual.body &&
-       git config submodule.subname.ignore all &&
-       git diff HEAD >actual2 &&
-       ! test -s actual2 &&
-       git config submodule.subname.ignore untracked &&
-       git diff HEAD >actual3 &&
-       sed -e "1,/^@@/d" actual3 >actual3.body &&
-       expect_from_to >expect.body $subprev $subprev-dirty &&
-       test_cmp expect.body actual3.body &&
-       git config submodule.subname.ignore dirty &&
-       git diff HEAD >actual4 &&
-       ! test -s actual4 &&
-       git diff HEAD --ignore-submodules=none >actual &&
-       sed -e "1,/^@@/d" actual >actual.body &&
-       expect_from_to >expect.body $subprev $subprev-dirty &&
-       test_cmp expect.body actual.body &&
-       git config --remove-section submodule.subname &&
-       git config --unset diff.ignoreSubmodules
-'
-
 test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.gitmodules]' '
        git config diff.ignoreSubmodules dirty &&
        git diff HEAD >actual &&
@@ -208,24 +179,6 @@ test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match)'
        ! test -s actual4
 '
 
-test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.git/config]' '
-       git config submodule.subname.ignore all &&
-       git config submodule.subname.path sub &&
-       git diff HEAD >actual2 &&
-       ! test -s actual2 &&
-       git config submodule.subname.ignore untracked &&
-       git diff HEAD >actual3 &&
-       ! test -s actual3 &&
-       git config submodule.subname.ignore dirty &&
-       git diff HEAD >actual4 &&
-       ! test -s actual4 &&
-       git diff --ignore-submodules=none HEAD >actual &&
-       sed -e "1,/^@@/d" actual >actual.body &&
-       expect_from_to >expect.body $subprev $subprev-dirty &&
-       test_cmp expect.body actual.body &&
-       git config --remove-section submodule.subname
-'
-
 test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.gitmodules]' '
        git config --add -f .gitmodules submodule.subname.ignore all &&
        git config --add -f .gitmodules submodule.subname.path sub &&
@@ -261,26 +214,6 @@ test_expect_success 'git diff between submodule commits' '
        ! test -s actual
 '
 
-test_expect_success 'git diff between submodule commits [.git/config]' '
-       git diff HEAD^..HEAD >actual &&
-       sed -e "1,/^@@/d" actual >actual.body &&
-       expect_from_to >expect.body $subtip $subprev &&
-       test_cmp expect.body actual.body &&
-       git config submodule.subname.ignore dirty &&
-       git config submodule.subname.path sub &&
-       git diff HEAD^..HEAD >actual &&
-       sed -e "1,/^@@/d" actual >actual.body &&
-       expect_from_to >expect.body $subtip $subprev &&
-       test_cmp expect.body actual.body &&
-       git config submodule.subname.ignore all &&
-       git diff HEAD^..HEAD >actual &&
-       ! test -s actual &&
-       git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
-       sed -e "1,/^@@/d" actual >actual.body &&
-       expect_from_to >expect.body $subtip $subprev &&
-       git config --remove-section submodule.subname
-'
-
 test_expect_success 'git diff between submodule commits [.gitmodules]' '
        git diff HEAD^..HEAD >actual &&
        sed -e "1,/^@@/d" actual >actual.body &&
index d350065f25c2fb8cd61d9f12f720cf315aa3436d..4fc27c51f7322ccb49a58d369185e64278ea246b 100755 (executable)
@@ -467,21 +467,42 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
        test_cmp one expect
 '
 
-test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
+test_expect_success 'CR-LF line endings && add line && text=auto' '
        git config --unset core.whitespace &&
        printf "a\r\n" >one &&
+       cp one save-one &&
+       git add one &&
        printf "b\r\n" >>one &&
-       printf "c\r\n" >>one &&
+       cp one expect &&
+       git diff -- one >patch &&
+       mv save-one one &&
+       echo "one text=auto" >.gitattributes &&
+       git apply patch &&
+       test_cmp one expect
+'
+
+test_expect_success 'CR-LF line endings && change line && text=auto' '
+       printf "a\r\n" >one &&
        cp one save-one &&
-       printf "                 \r\n" >>one &&
        git add one &&
+       printf "b\r\n" >one &&
        cp one expect &&
-       printf "d\r\n" >>one &&
        git diff -- one >patch &&
        mv save-one one &&
-       echo d >>expect &&
+       echo "one text=auto" >.gitattributes &&
+       git apply patch &&
+       test_cmp one expect
+'
 
-       git apply --ignore-space-change --whitespace=fix patch &&
+test_expect_success 'LF in repo, CRLF in worktree && change line && text=auto' '
+       printf "a\n" >one &&
+       git add one &&
+       printf "b\r\n" >one &&
+       git diff -- one >patch &&
+       printf "a\r\n" >one &&
+       echo "one text=auto" >.gitattributes &&
+       git -c core.eol=CRLF apply patch &&
+       printf "b\r\n" >expect &&
        test_cmp one expect
 '
 
index 1a080e782371e2d7ad489c706a54fce1553b9579..d97d2bebc9850c8ba96a8263ed081fced2ecc153 100755 (executable)
@@ -239,6 +239,43 @@ test_expect_success 'old records rest in peace' '
        ! test -f $rr2/preimage
 '
 
+rerere_gc_custom_expiry_test () {
+       five_days="$1" right_now="$2"
+       test_expect_success "rerere gc with custom expiry ($five_days, $right_now)" '
+               rm -fr .git/rr-cache &&
+               rr=.git/rr-cache/$_z40 &&
+               mkdir -p "$rr" &&
+               >"$rr/preimage" &&
+               >"$rr/postimage" &&
+
+               two_days_ago=$((-2*86400)) &&
+               test-chmtime =$two_days_ago "$rr/preimage" &&
+               test-chmtime =$two_days_ago "$rr/postimage" &&
+
+               find .git/rr-cache -type f | sort >original &&
+
+               git -c "gc.rerereresolved=$five_days" \
+                   -c "gc.rerereunresolved=$five_days" rerere gc &&
+               find .git/rr-cache -type f | sort >actual &&
+               test_cmp original actual &&
+
+               git -c "gc.rerereresolved=$five_days" \
+                   -c "gc.rerereunresolved=$right_now" rerere gc &&
+               find .git/rr-cache -type f | sort >actual &&
+               test_cmp original actual &&
+
+               git -c "gc.rerereresolved=$right_now" \
+                   -c "gc.rerereunresolved=$right_now" rerere gc &&
+               find .git/rr-cache -type f | sort >actual &&
+               >expect &&
+               test_cmp expect actual
+       '
+}
+
+rerere_gc_custom_expiry_test 5 0
+
+rerere_gc_custom_expiry_test 5.days.ago now
+
 test_expect_success 'setup: file2 added differently in two branches' '
        git reset --hard &&
 
@@ -419,24 +456,6 @@ count_pre_post () {
        test_line_count = "$2" actual
 }
 
-test_expect_success 'rerere gc' '
-       find .git/rr-cache -type f >original &&
-       xargs test-chmtime -172800 <original &&
-
-       git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc &&
-       find .git/rr-cache -type f >actual &&
-       test_cmp original actual &&
-
-       git -c gc.rerereresolved=5 -c gc.rerereunresolved=0 rerere gc &&
-       find .git/rr-cache -type f >actual &&
-       test_cmp original actual &&
-
-       git -c gc.rerereresolved=0 -c gc.rerereunresolved=0 rerere gc &&
-       find .git/rr-cache -type f >actual &&
-       >expect &&
-       test_cmp expect actual
-'
-
 merge_conflict_resolve () {
        git reset --hard &&
        test_must_fail git merge six.1 &&
@@ -446,6 +465,8 @@ merge_conflict_resolve () {
 }
 
 test_expect_success 'multiple identical conflicts' '
+       rm -fr .git/rr-cache &&
+       mkdir .git/rr-cache &&
        git reset --hard &&
 
        test_seq 1 6 >early &&
index 18aa1b5889749e706cba70c2a4f2633bcc9c9eb4..ec5f530102c04080b9d199269583cac9ab924c7e 100755 (executable)
@@ -539,25 +539,62 @@ cat >trailers <<EOF
 Signed-off-by: A U Thor <author@example.com>
 Acked-by: A U Thor <author@example.com>
 [ v2 updated patch description ]
-Signed-off-by: A U Thor <author@example.com>
+Signed-off-by: A U Thor
+  <author@example.com>
 EOF
 
-test_expect_success 'pretty format %(trailers) shows trailers' '
+unfold () {
+       perl -0pe 's/\n\s+/ /'
+}
+
+test_expect_success 'set up trailer tests' '
        echo "Some contents" >trailerfile &&
        git add trailerfile &&
-       git commit -F - <<-EOF &&
+       git commit -F - <<-EOF
        trailers: this commit message has trailers
 
        This commit is a test commit with trailers at the end. We parse this
-       message and display the trailers using %bT
+       message and display the trailers using %(trailers).
 
        $(cat trailers)
        EOF
+'
+
+test_expect_success 'pretty format %(trailers) shows trailers' '
        git log --no-walk --pretty="%(trailers)" >actual &&
-       cat >expect <<-EOF &&
-       $(cat trailers)
+       {
+               cat trailers &&
+               echo
+       } >expect &&
+       test_cmp expect actual
+'
 
-       EOF
+test_expect_success '%(trailers:only) shows only "key: value" trailers' '
+       git log --no-walk --pretty="%(trailers:only)" >actual &&
+       {
+               grep -v patch.description <trailers &&
+               echo
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '%(trailers:unfold) unfolds trailers' '
+       git log --no-walk --pretty="%(trailers:unfold)" >actual &&
+       {
+               unfold <trailers &&
+               echo
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success ':only and :unfold work together' '
+       git log --no-walk --pretty="%(trailers:only:unfold)" >actual &&
+       git log --no-walk --pretty="%(trailers:unfold:only)" >reverse &&
+       test_cmp actual reverse &&
+       {
+               grep -v patch.description <trailers | unfold &&
+               echo
+       } >expect &&
        test_cmp expect actual
 '
 
index b04d955bfa8229a9c864ad53d1f9f1ec06260637..897f6f06d55a9790a237b75c12eaab95763d1864 100755 (executable)
@@ -7,11 +7,15 @@ test_description='git archive attribute tests'
 SUBSTFORMAT='%H (%h)%n'
 
 test_expect_exists() {
-       test_expect_success " $1 exists" "test -e $1"
+       test_expect_${2:-success} " $1 exists" "test -e $1"
 }
 
 test_expect_missing() {
-       test_expect_success " $1 does not exist" "test ! -e $1"
+       test_expect_${2:-success} " $1 does not exist" "test ! -e $1"
+}
+
+extract_tar_to_dir () {
+       (mkdir "$1" && cd "$1" && "$TAR" xf -) <"$1.tar"
 }
 
 test_expect_success 'setup' '
@@ -21,12 +25,19 @@ test_expect_success 'setup' '
 
        echo ignored by tree >ignored-by-tree &&
        echo ignored-by-tree export-ignore >.gitattributes &&
-       git add ignored-by-tree .gitattributes &&
+       mkdir ignored-by-tree.d &&
+       >ignored-by-tree.d/file &&
+       echo ignored-by-tree.d export-ignore >>.gitattributes &&
+       git add ignored-by-tree ignored-by-tree.d .gitattributes &&
 
        echo ignored by worktree >ignored-by-worktree &&
        echo ignored-by-worktree export-ignore >.gitattributes &&
        git add ignored-by-worktree &&
 
+       mkdir excluded-by-pathspec.d &&
+       >excluded-by-pathspec.d/file &&
+       git add excluded-by-pathspec.d &&
+
        printf "A\$Format:%s\$O" "$SUBSTFORMAT" >nosubstfile &&
        printf "A\$Format:%s\$O" "$SUBSTFORMAT" >substfile1 &&
        printf "A not substituted O" >substfile2 &&
@@ -46,7 +57,37 @@ test_expect_success 'git archive' '
 
 test_expect_missing    archive/ignored
 test_expect_missing    archive/ignored-by-tree
+test_expect_missing    archive/ignored-by-tree.d
+test_expect_missing    archive/ignored-by-tree.d/file
 test_expect_exists     archive/ignored-by-worktree
+test_expect_exists     archive/excluded-by-pathspec.d
+test_expect_exists     archive/excluded-by-pathspec.d/file
+
+test_expect_success 'git archive with pathspec' '
+       git archive HEAD ":!excluded-by-pathspec.d" >archive-pathspec.tar &&
+       extract_tar_to_dir archive-pathspec
+'
+
+test_expect_missing    archive-pathspec/ignored
+test_expect_missing    archive-pathspec/ignored-by-tree
+test_expect_missing    archive-pathspec/ignored-by-tree.d
+test_expect_missing    archive-pathspec/ignored-by-tree.d/file
+test_expect_exists     archive-pathspec/ignored-by-worktree
+test_expect_missing    archive-pathspec/excluded-by-pathspec.d failure
+test_expect_missing    archive-pathspec/excluded-by-pathspec.d/file
+
+test_expect_success 'git archive with wildcard pathspec' '
+       git archive HEAD ":!excluded-by-p*" >archive-pathspec-wildcard.tar &&
+       extract_tar_to_dir archive-pathspec-wildcard
+'
+
+test_expect_missing    archive-pathspec-wildcard/ignored
+test_expect_missing    archive-pathspec-wildcard/ignored-by-tree
+test_expect_missing    archive-pathspec-wildcard/ignored-by-tree.d
+test_expect_missing    archive-pathspec-wildcard/ignored-by-tree.d/file
+test_expect_exists     archive-pathspec-wildcard/ignored-by-worktree
+test_expect_missing    archive-pathspec-wildcard/excluded-by-pathspec.d
+test_expect_missing    archive-pathspec-wildcard/excluded-by-pathspec.d/file
 
 test_expect_success 'git archive with worktree attributes' '
        git archive --worktree-attributes HEAD >worktree.tar &&
index 133b5842b1aee8e8e00eb0ca7e1b2d32ff4a4c9a..6694c19a1eecf10117b843bbacbc5bb47924c9c8 100755 (executable)
@@ -283,4 +283,41 @@ test_expect_success 'prune: handle alternate object database' '
        git -C B prune
 '
 
+test_expect_success 'prune: handle index in multiple worktrees' '
+       git worktree add second-worktree &&
+       echo "new blob for second-worktree" >second-worktree/blob &&
+       git -C second-worktree add blob &&
+       git prune --expire=now &&
+       git -C second-worktree show :blob >actual &&
+       test_cmp second-worktree/blob actual
+'
+
+test_expect_success 'prune: handle HEAD in multiple worktrees' '
+       git worktree add --detach third-worktree &&
+       echo "new blob for third-worktree" >third-worktree/blob &&
+       git -C third-worktree add blob &&
+       git -C third-worktree commit -m "third" &&
+       rm .git/worktrees/third-worktree/index &&
+       test_must_fail git -C third-worktree show :blob &&
+       git prune --expire=now &&
+       git -C third-worktree show HEAD:blob >actual &&
+       test_cmp third-worktree/blob actual
+'
+
+test_expect_success 'prune: handle HEAD reflog in multiple worktrees' '
+       git config core.logAllRefUpdates true &&
+       echo "lost blob for third-worktree" >expected &&
+       (
+               cd third-worktree &&
+               cat ../expected >blob &&
+               git add blob &&
+               git commit -m "second commit in third" &&
+               git reset --hard HEAD^
+       ) &&
+       git prune --expire=now &&
+       SHA1=`git hash-object expected` &&
+       git -C third-worktree show "$SHA1" >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 97a07655a0e0d74747259498bfbb8d03ea6c7480..8f17fd9da8ef6b54ae0a97500f2154b1c9d968b3 100755 (executable)
@@ -100,7 +100,7 @@ test_expect_success 'checkout (up-to-date with upstream)' '
        (
                cd test && git checkout b6
        ) >actual &&
-       test_i18ngrep "Your branch is up-to-date with .origin/master" actual
+       test_i18ngrep "Your branch is up to date with .origin/master" actual
 '
 
 test_expect_success 'status (diverged from upstream)' '
@@ -130,7 +130,7 @@ test_expect_success 'status (up-to-date with upstream)' '
                # reports nothing to commit
                test_must_fail git commit --dry-run
        ) >actual &&
-       test_i18ngrep "Your branch is up-to-date with .origin/master" actual
+       test_i18ngrep "Your branch is up to date with .origin/master" actual
 '
 
 cat >expect <<\EOF
@@ -188,35 +188,29 @@ test_expect_success 'fail to track annotated tags' '
        test_must_fail git checkout heavytrack
 '
 
-test_expect_success 'setup tracking with branch --set-upstream on existing branch' '
+test_expect_success '--set-upstream-to does not change branch' '
        git branch from-master master &&
-       test_must_fail git config branch.from-master.merge > actual &&
-       git branch --set-upstream from-master master &&
-       git config branch.from-master.merge > actual &&
-       grep -q "^refs/heads/master$" actual
-'
-
-test_expect_success '--set-upstream does not change branch' '
+       git branch --set-upstream-to master from-master &&
        git branch from-master2 master &&
        test_must_fail git config branch.from-master2.merge > actual &&
        git rev-list from-master2 &&
        git update-ref refs/heads/from-master2 from-master2^ &&
        git rev-parse from-master2 >expect2 &&
-       git branch --set-upstream from-master2 master &&
+       git branch --set-upstream-to master from-master2 &&
        git config branch.from-master.merge > actual &&
        git rev-parse from-master2 >actual2 &&
        grep -q "^refs/heads/master$" actual &&
        cmp expect2 actual2
 '
 
-test_expect_success '--set-upstream @{-1}' '
-       git checkout from-master &&
+test_expect_success '--set-upstream-to @{-1}' '
+       git checkout follower &&
        git checkout from-master2 &&
        git config branch.from-master2.merge > expect2 &&
-       git branch --set-upstream @{-1} follower &&
+       git branch --set-upstream-to @{-1} from-master &&
        git config branch.from-master.merge > actual &&
        git config branch.from-master2.merge > actual2 &&
-       git branch --set-upstream from-master follower &&
+       git branch --set-upstream-to follower from-master &&
        git config branch.from-master.merge > expect &&
        test_cmp expect2 actual2 &&
        test_cmp expect actual
index e9c3335b78bb6f78c640cf496f7b055462c1e275..6f8337ffb563cf13f194186b101bbaebf0beeb35 100755 (executable)
@@ -46,16 +46,6 @@ test_expect_success 'submodule update aborts on missing gitmodules url' '
        test_must_fail git submodule init
 '
 
-test_expect_success 'configuration parsing' '
-       test_when_finished "rm -f .gitmodules" &&
-       cat >.gitmodules <<-\EOF &&
-       [submodule "s"]
-               path
-               ignore
-       EOF
-       test_must_fail git status
-'
-
 test_expect_success 'setup - repository in init subdirectory' '
        mkdir init &&
        (
index eea36f1dbe37859c5b45ca18bb3c8f1080ff3f9a..46c09c77654597b2ee501271b4688cc6137df8f1 100755 (executable)
@@ -31,6 +31,21 @@ test_expect_success 'submodule config cache setup' '
        )
 '
 
+test_expect_success 'configuration parsing with error' '
+       test_when_finished "rm -rf repo" &&
+       test_create_repo repo &&
+       cat >repo/.gitmodules <<-\EOF &&
+       [submodule "s"]
+               path
+               ignore
+       EOF
+       (
+               cd repo &&
+               test_must_fail test-submodule-config "" s 2>actual &&
+               test_i18ngrep "bad config" actual
+       )
+'
+
 cat >super/expect <<EOF
 Submodule name: 'a' for path 'a'
 Submodule name: 'a' for path 'b'
@@ -107,78 +122,6 @@ test_expect_success 'using different treeishs works' '
        )
 '
 
-cat >super/expect_url <<EOF
-Submodule url: 'git@somewhere.else.net:a.git' for path 'b'
-Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
-EOF
-
-cat >super/expect_local_path <<EOF
-Submodule name: 'a' for path 'c'
-Submodule name: 'submodule' for path 'submodule'
-EOF
-
-test_expect_success 'reading of local configuration' '
-       (cd super &&
-               old_a=$(git config submodule.a.url) &&
-               old_submodule=$(git config submodule.submodule.url) &&
-               git config submodule.a.url git@somewhere.else.net:a.git &&
-               git config submodule.submodule.url git@somewhere.else.net:submodule.git &&
-               test-submodule-config --url \
-                       "" b \
-                       "" submodule \
-                               >actual &&
-               test_cmp expect_url actual &&
-               git config submodule.a.path c &&
-               test-submodule-config \
-                       "" c \
-                       "" submodule \
-                               >actual &&
-               test_cmp expect_local_path actual &&
-               git config submodule.a.url "$old_a" &&
-               git config submodule.submodule.url "$old_submodule" &&
-               git config --unset submodule.a.path c
-       )
-'
-
-cat >super/expect_url <<EOF
-Submodule url: '../submodule' for path 'b'
-Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
-EOF
-
-test_expect_success 'reading of local configuration for uninitialized submodules' '
-       (
-               cd super &&
-               git submodule deinit -f b &&
-               old_submodule=$(git config submodule.submodule.url) &&
-               git config submodule.submodule.url git@somewhere.else.net:submodule.git &&
-               test-submodule-config --url \
-                       "" b \
-                       "" submodule \
-                               >actual &&
-               test_cmp expect_url actual &&
-               git config submodule.submodule.url "$old_submodule" &&
-               git submodule init b
-       )
-'
-
-cat >super/expect_fetchrecurse_die.err <<EOF
-fatal: bad submodule.submodule.fetchrecursesubmodules argument: blabla
-EOF
-
-test_expect_success 'local error in fetchrecursesubmodule dies early' '
-       (cd super &&
-               git config submodule.submodule.fetchrecursesubmodules blabla &&
-               test_must_fail test-submodule-config \
-                       "" b \
-                       "" submodule \
-                               >actual.out 2>actual.err &&
-               touch expect_fetchrecurse_die.out &&
-               test_cmp expect_fetchrecurse_die.out actual.out  &&
-               test_cmp expect_fetchrecurse_die.err actual.err  &&
-               git config --unset submodule.submodule.fetchrecursesubmodules
-       )
-'
-
 test_expect_success 'error in history in fetchrecursesubmodule lets continue' '
        (cd super &&
                git config -f .gitmodules \
index 0c6f91c4338cce131b3d6066e590f177f7e739d1..164719d1c9d3e76a08bbbeb968aaf535370db7d1 100755 (executable)
@@ -681,6 +681,36 @@ test_expect_success 'using "where = before"' '
        test_cmp expected actual
 '
 
+test_expect_success 'overriding configuration with "--where after"' '
+       git config trailer.ack.where "before" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Fixes: Z
+               Acked-by= Z
+               Acked-by= Peff
+               Reviewed-by: Z
+               Signed-off-by: Z
+       EOF
+       git interpret-trailers --where after --trailer "ack: Peff" \
+               complex_message >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" with "--no-where"' '
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Bug #42
+               Fixes: Z
+               Acked-by= Peff
+               Acked-by= Z
+               Reviewed-by: Z
+               Signed-off-by: Z
+       EOF
+       git interpret-trailers --where after --no-where --trailer "ack: Peff" \
+               --trailer "bug: 42" complex_message >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'using "where = after"' '
        git config trailer.ack.where "after" &&
        cat complex_message_body >expected &&
@@ -947,6 +977,23 @@ test_expect_success 'using "ifExists = add" with "where = after"' '
        test_cmp expected actual
 '
 
+test_expect_success 'overriding configuration with "--if-exists replace"' '
+       git config trailer.fix.key "Fixes: " &&
+       git config trailer.fix.ifExists "add" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Bug #42
+               Acked-by= Z
+               Reviewed-by:
+               Signed-off-by: Z
+               Fixes: 22
+       EOF
+       git interpret-trailers --if-exists replace --trailer "review:" \
+               --trailer "fix=53" --trailer "fix=22" --trailer "bug: 42" \
+               <complex_message >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'using "ifExists = replace"' '
        git config trailer.fix.key "Fixes: " &&
        git config trailer.fix.ifExists "replace" &&
@@ -1026,6 +1073,25 @@ test_expect_success 'the default is "ifMissing = add"' '
        test_cmp expected actual
 '
 
+test_expect_success 'overriding configuration with "--if-missing doNothing"' '
+       git config trailer.ifmissing "add" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Fixes: Z
+               Acked-by= Z
+               Acked-by= Junio
+               Acked-by= Peff
+               Reviewed-by:
+               Signed-off-by: Z
+       EOF
+       git interpret-trailers --if-missing doNothing \
+               --trailer "review:" --trailer "fix=53" \
+               --trailer "cc=Linus" --trailer "ack: Junio" \
+               --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+               <complex_message >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'when default "ifMissing" is "doNothing"' '
        git config trailer.ifmissing "doNothing" &&
        cat complex_message_body >expected &&
@@ -1275,4 +1341,80 @@ test_expect_success 'with cut line' '
        test_cmp expected actual
 '
 
+test_expect_success 'only trailers' '
+       git config trailer.sign.command "echo config-value" &&
+       cat >expected <<-\EOF &&
+               existing: existing-value
+               sign: config-value
+               added: added-value
+       EOF
+       git interpret-trailers \
+               --trailer added:added-value \
+               --only-trailers >actual <<-\EOF &&
+               my subject
+
+               my body
+
+               existing: existing-value
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'only-trailers omits non-trailer in middle of block' '
+       git config trailer.sign.command "echo config-value" &&
+       cat >expected <<-\EOF &&
+               Signed-off-by: nobody <nobody@nowhere>
+               Signed-off-by: somebody <somebody@somewhere>
+               sign: config-value
+       EOF
+       git interpret-trailers --only-trailers >actual <<-\EOF &&
+               subject
+
+               it is important that the trailers below are signed-off-by
+               so that they meet the "25% trailers Git knows about" heuristic
+
+               Signed-off-by: nobody <nobody@nowhere>
+               this is not a trailer
+               Signed-off-by: somebody <somebody@somewhere>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'only input' '
+       git config trailer.sign.command "echo config-value" &&
+       cat >expected <<-\EOF &&
+               existing: existing-value
+       EOF
+       git interpret-trailers \
+               --only-trailers --only-input >actual <<-\EOF &&
+               my subject
+
+               my body
+
+               existing: existing-value
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'unfold' '
+       cat >expected <<-\EOF &&
+               foo: continued across several lines
+       EOF
+       # pass through tr to make leading and trailing whitespace more obvious
+       tr _ " " <<-\EOF |
+               my subject
+
+               my body
+
+               foo:_
+               __continued
+               ___across
+               ____several
+               _____lines
+               ___
+       EOF
+       git interpret-trailers --only-trailers --only-input --unfold >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 2ebda509ac337f99c7ab5651734a5b20d4a0b6f4..80194b79f90c800e7aaeecca9cd502e3ade494b4 100755 (executable)
@@ -774,4 +774,19 @@ test_expect_success 'merge can be completed with --continue' '
        verify_parents $c0 $c1
 '
 
+write_script .git/FAKE_EDITOR <<EOF
+# kill -TERM command added below.
+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
+         GIT_EDITOR=.git/FAKE_EDITOR
+         export GIT_EDITOR
+         exec git merge --no-ff --edit c1'\'' &&
+       git merge --continue &&
+       verify_parents $c0 $c1
+'
+
 test_done
diff --git a/t/t7614-merge-signoff.sh b/t/t7614-merge-signoff.sh
new file mode 100755 (executable)
index 0000000..c1b8446
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='git merge --signoff
+
+This test runs git merge --signoff and makes sure that it works.
+'
+
+. ./test-lib.sh
+
+# Setup test files
+test_setup() {
+       # Expected commit message after merge --signoff
+       cat >expected-signed <<EOF &&
+Merge branch 'master' into other-branch
+
+Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
+EOF
+
+       # Expected commit message after merge without --signoff (or with --no-signoff)
+       cat >expected-unsigned <<EOF &&
+Merge branch 'master' into other-branch
+EOF
+
+       # Initial commit and feature branch to merge master into it.
+       git commit --allow-empty -m "Initial empty commit" &&
+       git checkout -b other-branch &&
+       test_commit other-branch file1 1
+}
+
+# Setup repository, files & feature branch
+# This step must be run if You want to test 2,3 or 4
+# Order of 2,3,4 is not important, but 1 must be run before
+# For example `-r 1,4` or `-r 1,4,2 -v` etc
+# But not `-r 2` or `-r 4,3,2,1`
+test_expect_success 'setup' '
+       test_setup
+'
+
+# Test with --signoff flag
+test_expect_success 'git merge --signoff adds a sign-off line' '
+       git checkout master &&
+       test_commit master-branch-2 file2 2 &&
+       git checkout other-branch &&
+       git merge master --signoff --no-edit &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+       test_cmp expected-signed actual
+'
+
+# Test without --signoff flag
+test_expect_success 'git merge does not add a sign-off line' '
+       git checkout master &&
+       test_commit master-branch-3 file3 3 &&
+       git checkout other-branch &&
+       git merge master --no-edit &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+       test_cmp expected-unsigned actual
+'
+
+# Test for --no-signoff flag
+test_expect_success 'git merge --no-signoff flag cancels --signoff flag' '
+       git checkout master &&
+       test_commit master-branch-4 file4 4 &&
+       git checkout other-branch &&
+       git merge master --no-edit --signoff --no-signoff &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+       test_cmp expected-unsigned actual
+'
+
+test_done
index d1e4e8ad19f3d3e005e177b98e4eee16f4ef1461..f30980895c2f1b93317870e6b471d67bc99b0fd2 100755 (executable)
@@ -148,6 +148,8 @@ cat >expected-cc <<\EOF
 !two@example.com!
 !three@example.com!
 !four@example.com!
+!five@example.com!
+!six@example.com!
 EOF
 "
 
@@ -161,6 +163,8 @@ test_expect_success $PREREQ 'cc trailer with various syntax' '
        Cc: <two@example.com> # trailing comments are ignored
        Cc: <three@example.com>, <not.four@example.com> one address per line
        Cc: "Some # Body" <four@example.com> [ <also.a.comment> ]
+       Cc: five@example.com # not.six@example.com
+       Cc: six@example.com, not.seven@example.com
        EOF
        clean_fake_sendmail &&
        git send-email -1 --to=recipient@example.com \
index 68437106703470f39a6079aaad7d3bb407ac3389..5fdafdd2d2d72390ee9fe3c2afd501ad222fac8e 100644 (file)
  *     `fdopen_tempfile()` has been called on the object
  *   - `owner` holds the PID of the process that created the file
  *
- * - Active, file closed (after successful `close_tempfile()`). Same
+ * - Active, file closed (after `close_tempfile_gently()`). Same
  *   as the previous state, except that the temporary file is closed,
  *   `fd` is -1, and `fp` is `NULL`.
  *
- * - Inactive (after `delete_tempfile()`, `rename_tempfile()`, a
- *   failed attempt to create a temporary file, or a failed
- *   `close_tempfile()`). In this state:
+ * - Inactive (after `delete_tempfile()`, `rename_tempfile()`, or a
+ *   failed attempt to create a temporary file). In this state:
  *
  *   - `active` is unset
  *   - `filename` is empty (usually, though there are transitory
  *     states in which this condition doesn't hold). Client code should
  *     *not* rely on the filename being empty in this state.
  *   - `fd` is -1 and `fp` is `NULL`
- *   - the object is left registered in the `tempfile_list`, and
- *     `on_list` is set.
+ *   - the object is removed from `tempfile_list` (but could be used again)
  *
  * A temporary file is owned by the process that created it. The
  * `tempfile` has an `owner` field that records the owner's PID. This
 #include "tempfile.h"
 #include "sigchain.h"
 
-static struct tempfile *volatile tempfile_list;
+static VOLATILE_LIST_HEAD(tempfile_list);
 
-static void remove_tempfiles(int skip_fclose)
+static void remove_tempfiles(int in_signal_handler)
 {
        pid_t me = getpid();
+       volatile struct volatile_list_head *pos;
 
-       while (tempfile_list) {
-               if (tempfile_list->owner == me) {
-                       /* fclose() is not safe to call in a signal handler */
-                       if (skip_fclose)
-                               tempfile_list->fp = NULL;
-                       delete_tempfile(tempfile_list);
-               }
-               tempfile_list = tempfile_list->next;
+       list_for_each(pos, &tempfile_list) {
+               struct tempfile *p = list_entry(pos, struct tempfile, list);
+
+               if (!is_tempfile_active(p) || p->owner != me)
+                       continue;
+
+               if (p->fd >= 0)
+                       close(p->fd);
+
+               if (in_signal_handler)
+                       unlink(p->filename.buf);
+               else
+                       unlink_or_warn(p->filename.buf);
+
+               p->active = 0;
        }
 }
 
@@ -85,39 +91,48 @@ static void remove_tempfiles_on_signal(int signo)
        raise(signo);
 }
 
-/*
- * Initialize *tempfile if necessary and add it to tempfile_list.
- */
-static void prepare_tempfile_object(struct tempfile *tempfile)
+static struct tempfile *new_tempfile(void)
 {
-       if (!tempfile_list) {
-               /* One-time initialization */
+       struct tempfile *tempfile = xmalloc(sizeof(*tempfile));
+       tempfile->fd = -1;
+       tempfile->fp = NULL;
+       tempfile->active = 0;
+       tempfile->owner = 0;
+       INIT_LIST_HEAD(&tempfile->list);
+       strbuf_init(&tempfile->filename, 0);
+       return tempfile;
+}
+
+static void activate_tempfile(struct tempfile *tempfile)
+{
+       static int initialized;
+
+       if (is_tempfile_active(tempfile))
+               BUG("activate_tempfile called for active object");
+
+       if (!initialized) {
                sigchain_push_common(remove_tempfiles_on_signal);
                atexit(remove_tempfiles_on_exit);
+               initialized = 1;
        }
 
-       if (tempfile->active)
-               die("BUG: prepare_tempfile_object called for active object");
-       if (!tempfile->on_list) {
-               /* Initialize *tempfile and add it to tempfile_list: */
-               tempfile->fd = -1;
-               tempfile->fp = NULL;
-               tempfile->active = 0;
-               tempfile->owner = 0;
-               strbuf_init(&tempfile->filename, 0);
-               tempfile->next = tempfile_list;
-               tempfile_list = tempfile;
-               tempfile->on_list = 1;
-       } else if (tempfile->filename.len) {
-               /* This shouldn't happen, but better safe than sorry. */
-               die("BUG: prepare_tempfile_object called for improperly-reset object");
-       }
+       volatile_list_add(&tempfile->list, &tempfile_list);
+       tempfile->owner = getpid();
+       tempfile->active = 1;
+}
+
+static void deactivate_tempfile(struct tempfile *tempfile)
+{
+       tempfile->active = 0;
+       strbuf_release(&tempfile->filename);
+       volatile_list_del(&tempfile->list);
+       free(tempfile);
 }
 
 /* Make sure errno contains a meaningful value on error */
-int create_tempfile(struct tempfile *tempfile, const char *path)
+struct tempfile *create_tempfile(const char *path)
 {
-       prepare_tempfile_object(tempfile);
+       struct tempfile *tempfile = new_tempfile();
 
        strbuf_add_absolute_path(&tempfile->filename, path);
        tempfile->fd = open(tempfile->filename.buf,
@@ -127,52 +142,48 @@ int create_tempfile(struct tempfile *tempfile, const char *path)
                tempfile->fd = open(tempfile->filename.buf,
                                    O_RDWR | O_CREAT | O_EXCL, 0666);
        if (tempfile->fd < 0) {
-               strbuf_reset(&tempfile->filename);
-               return -1;
+               deactivate_tempfile(tempfile);
+               return NULL;
        }
-       tempfile->owner = getpid();
-       tempfile->active = 1;
+       activate_tempfile(tempfile);
        if (adjust_shared_perm(tempfile->filename.buf)) {
                int save_errno = errno;
                error("cannot fix permission bits on %s", tempfile->filename.buf);
-               delete_tempfile(tempfile);
+               delete_tempfile(&tempfile);
                errno = save_errno;
-               return -1;
+               return NULL;
        }
-       return tempfile->fd;
+
+       return tempfile;
 }
 
-void register_tempfile(struct tempfile *tempfile, const char *path)
+struct tempfile *register_tempfile(const char *path)
 {
-       prepare_tempfile_object(tempfile);
+       struct tempfile *tempfile = new_tempfile();
        strbuf_add_absolute_path(&tempfile->filename, path);
-       tempfile->owner = getpid();
-       tempfile->active = 1;
+       activate_tempfile(tempfile);
+       return tempfile;
 }
 
-int mks_tempfile_sm(struct tempfile *tempfile,
-                   const char *template, int suffixlen, int mode)
+struct tempfile *mks_tempfile_sm(const char *template, int suffixlen, int mode)
 {
-       prepare_tempfile_object(tempfile);
+       struct tempfile *tempfile = new_tempfile();
 
        strbuf_add_absolute_path(&tempfile->filename, template);
        tempfile->fd = git_mkstemps_mode(tempfile->filename.buf, suffixlen, mode);
        if (tempfile->fd < 0) {
-               strbuf_reset(&tempfile->filename);
-               return -1;
+               deactivate_tempfile(tempfile);
+               return NULL;
        }
-       tempfile->owner = getpid();
-       tempfile->active = 1;
-       return tempfile->fd;
+       activate_tempfile(tempfile);
+       return tempfile;
 }
 
-int mks_tempfile_tsm(struct tempfile *tempfile,
-                    const char *template, int suffixlen, int mode)
+struct tempfile *mks_tempfile_tsm(const char *template, int suffixlen, int mode)
 {
+       struct tempfile *tempfile = new_tempfile();
        const char *tmpdir;
 
-       prepare_tempfile_object(tempfile);
-
        tmpdir = getenv("TMPDIR");
        if (!tmpdir)
                tmpdir = "/tmp";
@@ -180,35 +191,34 @@ int mks_tempfile_tsm(struct tempfile *tempfile,
        strbuf_addf(&tempfile->filename, "%s/%s", tmpdir, template);
        tempfile->fd = git_mkstemps_mode(tempfile->filename.buf, suffixlen, mode);
        if (tempfile->fd < 0) {
-               strbuf_reset(&tempfile->filename);
-               return -1;
+               deactivate_tempfile(tempfile);
+               return NULL;
        }
-       tempfile->owner = getpid();
-       tempfile->active = 1;
-       return tempfile->fd;
+       activate_tempfile(tempfile);
+       return tempfile;
 }
 
-int xmks_tempfile_m(struct tempfile *tempfile, const char *template, int mode)
+struct tempfile *xmks_tempfile_m(const char *template, int mode)
 {
-       int fd;
+       struct tempfile *tempfile;
        struct strbuf full_template = STRBUF_INIT;
 
        strbuf_add_absolute_path(&full_template, template);
-       fd = mks_tempfile_m(tempfile, full_template.buf, mode);
-       if (fd < 0)
+       tempfile = mks_tempfile_m(full_template.buf, mode);
+       if (!tempfile)
                die_errno("Unable to create temporary file '%s'",
                          full_template.buf);
 
        strbuf_release(&full_template);
-       return fd;
+       return tempfile;
 }
 
 FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode)
 {
-       if (!tempfile->active)
-               die("BUG: fdopen_tempfile() called for inactive object");
+       if (!is_tempfile_active(tempfile))
+               BUG("fdopen_tempfile() called for inactive object");
        if (tempfile->fp)
-               die("BUG: fdopen_tempfile() called for open object");
+               BUG("fdopen_tempfile() called for open object");
 
        tempfile->fp = fdopen(tempfile->fd, mode);
        return tempfile->fp;
@@ -216,34 +226,36 @@ FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode)
 
 const char *get_tempfile_path(struct tempfile *tempfile)
 {
-       if (!tempfile->active)
-               die("BUG: get_tempfile_path() called for inactive object");
+       if (!is_tempfile_active(tempfile))
+               BUG("get_tempfile_path() called for inactive object");
        return tempfile->filename.buf;
 }
 
 int get_tempfile_fd(struct tempfile *tempfile)
 {
-       if (!tempfile->active)
-               die("BUG: get_tempfile_fd() called for inactive object");
+       if (!is_tempfile_active(tempfile))
+               BUG("get_tempfile_fd() called for inactive object");
        return tempfile->fd;
 }
 
 FILE *get_tempfile_fp(struct tempfile *tempfile)
 {
-       if (!tempfile->active)
-               die("BUG: get_tempfile_fp() called for inactive object");
+       if (!is_tempfile_active(tempfile))
+               BUG("get_tempfile_fp() called for inactive object");
        return tempfile->fp;
 }
 
-int close_tempfile(struct tempfile *tempfile)
+int close_tempfile_gently(struct tempfile *tempfile)
 {
-       int fd = tempfile->fd;
-       FILE *fp = tempfile->fp;
+       int fd;
+       FILE *fp;
        int err;
 
-       if (fd < 0)
+       if (!is_tempfile_active(tempfile) || tempfile->fd < 0)
                return 0;
 
+       fd = tempfile->fd;
+       fp = tempfile->fp;
        tempfile->fd = -1;
        if (fp) {
                tempfile->fp = NULL;
@@ -258,54 +270,52 @@ int close_tempfile(struct tempfile *tempfile)
                err = close(fd);
        }
 
-       if (err) {
-               int save_errno = errno;
-               delete_tempfile(tempfile);
-               errno = save_errno;
-               return -1;
-       }
-
-       return 0;
+       return err ? -1 : 0;
 }
 
 int reopen_tempfile(struct tempfile *tempfile)
 {
+       if (!is_tempfile_active(tempfile))
+               BUG("reopen_tempfile called for an inactive object");
        if (0 <= tempfile->fd)
-               die("BUG: reopen_tempfile called for an open object");
-       if (!tempfile->active)
-               die("BUG: reopen_tempfile called for an inactive object");
+               BUG("reopen_tempfile called for an open object");
        tempfile->fd = open(tempfile->filename.buf, O_WRONLY);
        return tempfile->fd;
 }
 
-int rename_tempfile(struct tempfile *tempfile, const char *path)
+int rename_tempfile(struct tempfile **tempfile_p, const char *path)
 {
-       if (!tempfile->active)
-               die("BUG: rename_tempfile called for inactive object");
+       struct tempfile *tempfile = *tempfile_p;
+
+       if (!is_tempfile_active(tempfile))
+               BUG("rename_tempfile called for inactive object");
 
-       if (close_tempfile(tempfile))
+       if (close_tempfile_gently(tempfile)) {
+               delete_tempfile(tempfile_p);
                return -1;
+       }
 
        if (rename(tempfile->filename.buf, path)) {
                int save_errno = errno;
-               delete_tempfile(tempfile);
+               delete_tempfile(tempfile_p);
                errno = save_errno;
                return -1;
        }
 
-       tempfile->active = 0;
-       strbuf_reset(&tempfile->filename);
+       deactivate_tempfile(tempfile);
+       *tempfile_p = NULL;
        return 0;
 }
 
-void delete_tempfile(struct tempfile *tempfile)
+void delete_tempfile(struct tempfile **tempfile_p)
 {
-       if (!tempfile->active)
+       struct tempfile *tempfile = *tempfile_p;
+
+       if (!is_tempfile_active(tempfile))
                return;
 
-       if (!close_tempfile(tempfile)) {
-               unlink_or_warn(tempfile->filename.buf);
-               tempfile->active = 0;
-               strbuf_reset(&tempfile->filename);
-       }
+       close_tempfile_gently(tempfile);
+       unlink_or_warn(tempfile->filename.buf);
+       deactivate_tempfile(tempfile);
+       *tempfile_p = NULL;
 }
index 2f0038decd5b6d00b55fa03ec8988a3810d1784f..b8f4b5e145b8b6b1d80cac299dbaedc8253e861b 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef TEMPFILE_H
 #define TEMPFILE_H
 
+#include "list.h"
+
 /*
  * Handle temporary files.
  *
  *
  * The caller:
  *
- * * Allocates a `struct tempfile` either as a static variable or on
- *   the heap, initialized to zeros. Once you use the structure to
- *   call `create_tempfile()`, it belongs to the tempfile subsystem
- *   and its storage must remain valid throughout the life of the
- *   program (i.e. you cannot use an on-stack variable to hold this
- *   structure).
- *
  * * Attempts to create a temporary file by calling
- *   `create_tempfile()`.
+ *   `create_tempfile()`. The resources used for the temporary file are
+ *   managed by the tempfile API.
  *
  * * Writes new content to the file by either:
  *
- *   * writing to the file descriptor returned by `create_tempfile()`
- *     (also available via `tempfile->fd`).
+ *   * writing to the `tempfile->fd` file descriptor
  *
  *   * calling `fdopen_tempfile()` to get a `FILE` pointer for the
  *     open file and writing to the file using stdio.
  *
- *   Note that the file descriptor returned by create_tempfile()
+ *   Note that the file descriptor created by create_tempfile()
  *   is marked O_CLOEXEC, so the new contents must be written by
  *   the current process, not any spawned one.
  *
  *   control of the file.
  *
  * * Close the file descriptor without removing or renaming the
- *   temporary file by calling `close_tempfile()`, and later call
+ *   temporary file by calling `close_tempfile_gently()`, and later call
  *   `delete_tempfile()` or `rename_tempfile()`.
  *
- * Even after the temporary file is renamed or deleted, the `tempfile`
- * object must not be freed or altered by the caller. However, it may
- * be reused; just pass it to another call of `create_tempfile()`.
+ * After the temporary file is renamed or deleted, the `tempfile`
+ * object is no longer valid and should not be reused.
  *
  * If the program exits before `rename_tempfile()` or
  * `delete_tempfile()` is called, an `atexit(3)` handler will close
  * and remove the temporary file.
  *
  * If you need to close the file descriptor yourself, do so by calling
- * `close_tempfile()`. You should never call `close(2)` or `fclose(3)`
+ * `close_tempfile_gently()`. You should never call `close(2)` or `fclose(3)`
  * yourself, otherwise the `struct tempfile` structure would still
  * think that the file descriptor needs to be closed, and a later
  * cleanup would result in duplicate calls to `close(2)`. Worse yet,
  * Error handling
  * --------------
  *
- * `create_tempfile()` returns a file descriptor on success or -1 on
- * failure. On errors, `errno` describes the reason for failure.
+ * `create_tempfile()` returns an allocated tempfile on success or NULL
+ * on failure. On errors, `errno` describes the reason for failure.
  *
- * `delete_tempfile()`, `rename_tempfile()`, and `close_tempfile()`
- * return 0 on success. On failure they set `errno` appropriately, do
- * their best to delete the temporary file, and return -1.
+ * `delete_tempfile()`, `rename_tempfile()`, and `close_tempfile_gently()`
+ * return 0 on success. On failure they set `errno` appropriately and return
+ * -1. `delete` and `rename` (but not `close`) do their best to delete the
+ * temporary file before returning.
  */
 
 struct tempfile {
-       struct tempfile *volatile next;
+       volatile struct volatile_list_head list;
        volatile sig_atomic_t active;
        volatile int fd;
        FILE *volatile fp;
        volatile pid_t owner;
-       char on_list;
        struct strbuf filename;
 };
 
 /*
  * Attempt to create a temporary file at the specified `path`. Return
- * a file descriptor for writing to it, or -1 on error. It is an error
- * if a file already exists at that path.
+ * 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 int create_tempfile(struct tempfile *tempfile, const char *path);
+extern struct tempfile *create_tempfile(const char *path);
 
 /*
  * Register an existing file as a tempfile, meaning that it will be
@@ -102,7 +96,7 @@ extern int create_tempfile(struct tempfile *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 void register_tempfile(struct tempfile *tempfile, const char *path);
+extern struct tempfile *register_tempfile(const char *path);
 
 
 /*
@@ -134,83 +128,78 @@ extern void register_tempfile(struct tempfile *tempfile, const char *path);
  * know the (absolute) path of the file that was created, it can be
  * read from tempfile->filename.
  *
- * On success, the functions return a file descriptor that is open for
- * writing the temporary file. On errors, they return -1 and set errno
- * appropriately (except for the "x" variants, which die() on errors).
+ * On success, the functions return a tempfile whose "fd" member is open
+ * for writing the temporary file. On errors, they return NULL and set
+ * errno appropriately (except for the "x" variants, which die() on
+ * errors).
  */
 
 /* See "mks_tempfile functions" above. */
-extern int mks_tempfile_sm(struct tempfile *tempfile,
-                          const char *template, int suffixlen, int mode);
+extern struct tempfile *mks_tempfile_sm(const char *template,
+                                       int suffixlen, int mode);
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile_s(struct tempfile *tempfile,
-                                const char *template, int suffixlen)
+static inline struct tempfile *mks_tempfile_s(const char *template,
+                                             int suffixlen)
 {
-       return mks_tempfile_sm(tempfile, template, suffixlen, 0600);
+       return mks_tempfile_sm(template, suffixlen, 0600);
 }
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile_m(struct tempfile *tempfile,
-                                const char *template, int mode)
+static inline struct tempfile *mks_tempfile_m(const char *template, int mode)
 {
-       return mks_tempfile_sm(tempfile, template, 0, mode);
+       return mks_tempfile_sm(template, 0, mode);
 }
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile(struct tempfile *tempfile,
-                              const char *template)
+static inline struct tempfile *mks_tempfile(const char *template)
 {
-       return mks_tempfile_sm(tempfile, template, 0, 0600);
+       return mks_tempfile_sm(template, 0, 0600);
 }
 
 /* See "mks_tempfile functions" above. */
-extern int mks_tempfile_tsm(struct tempfile *tempfile,
-                           const char *template, int suffixlen, int mode);
+extern struct tempfile *mks_tempfile_tsm(const char *template,
+                                        int suffixlen, int mode);
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile_ts(struct tempfile *tempfile,
-                                 const char *template, int suffixlen)
+static inline struct tempfile *mks_tempfile_ts(const char *template,
+                                              int suffixlen)
 {
-       return mks_tempfile_tsm(tempfile, template, suffixlen, 0600);
+       return mks_tempfile_tsm(template, suffixlen, 0600);
 }
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile_tm(struct tempfile *tempfile,
-                                 const char *template, int mode)
+static inline struct tempfile *mks_tempfile_tm(const char *template, int mode)
 {
-       return mks_tempfile_tsm(tempfile, template, 0, mode);
+       return mks_tempfile_tsm(template, 0, mode);
 }
 
 /* See "mks_tempfile functions" above. */
-static inline int mks_tempfile_t(struct tempfile *tempfile,
-                                const char *template)
+static inline struct tempfile *mks_tempfile_t(const char *template)
 {
-       return mks_tempfile_tsm(tempfile, template, 0, 0600);
+       return mks_tempfile_tsm(template, 0, 0600);
 }
 
 /* See "mks_tempfile functions" above. */
-extern int xmks_tempfile_m(struct tempfile *tempfile,
-                          const char *template, int mode);
+extern struct tempfile *xmks_tempfile_m(const char *template, int mode);
 
 /* See "mks_tempfile functions" above. */
-static inline int xmks_tempfile(struct tempfile *tempfile,
-                               const char *template)
+static inline struct tempfile *xmks_tempfile(const char *template)
 {
-       return xmks_tempfile_m(tempfile, template, 0600);
+       return xmks_tempfile_m(template, 0600);
 }
 
 /*
  * Associate a stdio stream with the temporary file (which must still
  * be open). Return `NULL` (*without* deleting the file) on error. The
- * stream is closed automatically when `close_tempfile()` is called or
+ * 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);
 
 static inline int is_tempfile_active(struct tempfile *tempfile)
 {
-       return tempfile->active;
+       return tempfile && tempfile->active;
 }
 
 /*
@@ -226,20 +215,20 @@ extern FILE *get_tempfile_fp(struct tempfile *tempfile);
  * If the temporary file is still open, close it (and the file pointer
  * too, if it has been opened using `fdopen_tempfile()`) without
  * deleting the file. Return 0 upon success. On failure to `close(2)`,
- * return a negative value and delete the file. Usually
- * `delete_tempfile()` or `rename_tempfile()` should eventually be
- * called if `close_tempfile()` succeeds.
+ * return a negative value. Usually `delete_tempfile()` or `rename_tempfile()`
+ * should eventually be called regardless of whether `close_tempfile_gently()`
+ * succeeds.
  */
-extern int close_tempfile(struct tempfile *tempfile);
+extern int close_tempfile_gently(struct tempfile *tempfile);
 
 /*
  * Re-open a temporary file that has been closed using
- * `close_tempfile()` but not yet deleted or renamed. This can be used
+ * `close_tempfile_gently()` but not yet deleted or renamed. This can be used
  * to implement a sequence of operations like the following:
  *
  * * Create temporary file.
  *
- * * Write new contents to file, then `close_tempfile()` to cause the
+ * * Write new contents to file, then `close_tempfile_gently()` to cause the
  *   contents to be written to disk.
  *
  * * Pass the name of the temporary file to another program to allow
@@ -259,7 +248,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);
+extern void delete_tempfile(struct tempfile **tempfile_p);
 
 /*
  * Close the file descriptor and/or file pointer if they are still
@@ -270,6 +259,6 @@ extern void delete_tempfile(struct tempfile *tempfile);
  * `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, const char *path);
+extern int rename_tempfile(struct tempfile **tempfile_p, const char *path);
 
 #endif /* TEMPFILE_H */
index b7f81c198e22e4ce578a6148fa3346becabbfb3b..db5feab8a1ee6433108ade9e165956678a3e260b 100755 (executable)
@@ -58,7 +58,7 @@ then
        not_in_topic=`git rev-list "^$topic" master`
        if test -z "$not_in_topic"
        then
-               echo >&2 "$topic is already up-to-date with master"
+               echo >&2 "$topic is already up to date with master"
                exit 1 ;# we could allow it, but there is no point.
        else
                exit 0
index 751b56c009a8c2f6c3531dab65565f9e06029778..3ba157ed0d6157281f0a9e91f7b7f208652adfcc 100644 (file)
--- a/trailer.c
+++ b/trailer.c
  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
  */
 
-enum action_where { WHERE_END, WHERE_AFTER, WHERE_BEFORE, WHERE_START };
-enum action_if_exists { EXISTS_ADD_IF_DIFFERENT_NEIGHBOR, EXISTS_ADD_IF_DIFFERENT,
-                       EXISTS_ADD, EXISTS_REPLACE, EXISTS_DO_NOTHING };
-enum action_if_missing { MISSING_ADD, MISSING_DO_NOTHING };
-
 struct conf_info {
        char *name;
        char *key;
        char *command;
-       enum action_where where;
-       enum action_if_exists if_exists;
-       enum action_if_missing if_missing;
+       enum trailer_where where;
+       enum trailer_if_exists if_exists;
+       enum trailer_if_missing if_missing;
 };
 
 static struct conf_info default_conf_info;
@@ -63,7 +58,7 @@ static const char *git_generated_prefixes[] = {
                pos != (head); \
                pos = is_reverse ? pos->prev : pos->next)
 
-static int after_or_end(enum action_where where)
+static int after_or_end(enum trailer_where where)
 {
        return (where == WHERE_AFTER) || (where == WHERE_END);
 }
@@ -164,13 +159,15 @@ static void print_tok_val(FILE *outfile, const char *tok, const char *val)
                fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
 }
 
-static void print_all(FILE *outfile, struct list_head *head, int trim_empty)
+static void print_all(FILE *outfile, struct list_head *head,
+                     const struct process_trailer_options *opts)
 {
        struct list_head *pos;
        struct trailer_item *item;
        list_for_each(pos, head) {
                item = list_entry(pos, struct trailer_item, list);
-               if (!trim_empty || strlen(item->value) > 0)
+               if ((!opts->trim_empty || strlen(item->value) > 0) &&
+                   (!opts->only_trailers || item->token))
                        print_tok_val(outfile, item->token, item->value);
        }
 }
@@ -201,7 +198,7 @@ static int check_if_different(struct trailer_item *in_tok,
                              int check_all,
                              struct list_head *head)
 {
-       enum action_where where = arg_tok->conf.where;
+       enum trailer_where where = arg_tok->conf.where;
        struct list_head *next_head;
        do {
                if (same_trailer(in_tok, arg_tok))
@@ -300,13 +297,16 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,
                else
                        free_arg_item(arg_tok);
                break;
+       default:
+               die("BUG: trailer.c: unhandled value %d",
+                   arg_tok->conf.if_exists);
        }
 }
 
 static void apply_arg_if_missing(struct list_head *head,
                                 struct arg_item *arg_tok)
 {
-       enum action_where where;
+       enum trailer_where where;
        struct trailer_item *to_add;
 
        switch (arg_tok->conf.if_missing) {
@@ -321,6 +321,10 @@ static void apply_arg_if_missing(struct list_head *head,
                        list_add_tail(&to_add->list, head);
                else
                        list_add(&to_add->list, head);
+               break;
+       default:
+               die("BUG: trailer.c: unhandled value %d",
+                   arg_tok->conf.if_missing);
        }
 }
 
@@ -331,7 +335,7 @@ static int find_same_and_apply_arg(struct list_head *head,
        struct trailer_item *in_tok;
        struct trailer_item *on_tok;
 
-       enum action_where where = arg_tok->conf.where;
+       enum trailer_where where = arg_tok->conf.where;
        int middle = (where == WHERE_AFTER) || (where == WHERE_BEFORE);
        int backwards = after_or_end(where);
        struct trailer_item *start_tok;
@@ -373,44 +377,50 @@ static void process_trailers_lists(struct list_head *head,
        }
 }
 
-static int set_where(struct conf_info *item, const char *value)
+int trailer_set_where(enum trailer_where *item, const char *value)
 {
-       if (!strcasecmp("after", value))
-               item->where = WHERE_AFTER;
+       if (!value)
+               *item = WHERE_DEFAULT;
+       else if (!strcasecmp("after", value))
+               *item = WHERE_AFTER;
        else if (!strcasecmp("before", value))
-               item->where = WHERE_BEFORE;
+               *item = WHERE_BEFORE;
        else if (!strcasecmp("end", value))
-               item->where = WHERE_END;
+               *item = WHERE_END;
        else if (!strcasecmp("start", value))
-               item->where = WHERE_START;
+               *item = WHERE_START;
        else
                return -1;
        return 0;
 }
 
-static int set_if_exists(struct conf_info *item, const char *value)
+int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)
 {
-       if (!strcasecmp("addIfDifferent", value))
-               item->if_exists = EXISTS_ADD_IF_DIFFERENT;
+       if (!value)
+               *item = EXISTS_DEFAULT;
+       else if (!strcasecmp("addIfDifferent", value))
+               *item = EXISTS_ADD_IF_DIFFERENT;
        else if (!strcasecmp("addIfDifferentNeighbor", value))
-               item->if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
+               *item = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
        else if (!strcasecmp("add", value))
-               item->if_exists = EXISTS_ADD;
+               *item = EXISTS_ADD;
        else if (!strcasecmp("replace", value))
-               item->if_exists = EXISTS_REPLACE;
+               *item = EXISTS_REPLACE;
        else if (!strcasecmp("doNothing", value))
-               item->if_exists = EXISTS_DO_NOTHING;
+               *item = EXISTS_DO_NOTHING;
        else
                return -1;
        return 0;
 }
 
-static int set_if_missing(struct conf_info *item, const char *value)
+int trailer_set_if_missing(enum trailer_if_missing *item, const char *value)
 {
-       if (!strcasecmp("doNothing", value))
-               item->if_missing = MISSING_DO_NOTHING;
+       if (!value)
+               *item = MISSING_DEFAULT;
+       else if (!strcasecmp("doNothing", value))
+               *item = MISSING_DO_NOTHING;
        else if (!strcasecmp("add", value))
-               item->if_missing = MISSING_ADD;
+               *item = MISSING_ADD;
        else
                return -1;
        return 0;
@@ -470,15 +480,18 @@ static int git_trailer_default_config(const char *conf_key, const char *value, v
        variable_name = strrchr(trailer_item, '.');
        if (!variable_name) {
                if (!strcmp(trailer_item, "where")) {
-                       if (set_where(&default_conf_info, value) < 0)
+                       if (trailer_set_where(&default_conf_info.where,
+                                             value) < 0)
                                warning(_("unknown value '%s' for key '%s'"),
                                        value, conf_key);
                } else if (!strcmp(trailer_item, "ifexists")) {
-                       if (set_if_exists(&default_conf_info, value) < 0)
+                       if (trailer_set_if_exists(&default_conf_info.if_exists,
+                                                 value) < 0)
                                warning(_("unknown value '%s' for key '%s'"),
                                        value, conf_key);
                } else if (!strcmp(trailer_item, "ifmissing")) {
-                       if (set_if_missing(&default_conf_info, value) < 0)
+                       if (trailer_set_if_missing(&default_conf_info.if_missing,
+                                                  value) < 0)
                                warning(_("unknown value '%s' for key '%s'"),
                                        value, conf_key);
                } else if (!strcmp(trailer_item, "separators")) {
@@ -532,15 +545,15 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
                conf->command = xstrdup(value);
                break;
        case TRAILER_WHERE:
-               if (set_where(conf, value))
+               if (trailer_set_where(&conf->where, value))
                        warning(_("unknown value '%s' for key '%s'"), value, conf_key);
                break;
        case TRAILER_IF_EXISTS:
-               if (set_if_exists(conf, value))
+               if (trailer_set_if_exists(&conf->if_exists, value))
                        warning(_("unknown value '%s' for key '%s'"), value, conf_key);
                break;
        case TRAILER_IF_MISSING:
-               if (set_if_missing(conf, value))
+               if (trailer_set_if_missing(&conf->if_missing, value))
                        warning(_("unknown value '%s' for key '%s'"), value, conf_key);
                break;
        default:
@@ -555,6 +568,9 @@ static void ensure_configured(void)
                return;
 
        /* Default config must be setup first */
+       default_conf_info.where = WHERE_END;
+       default_conf_info.if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
+       default_conf_info.if_missing = MISSING_ADD;
        git_config(git_trailer_default_config, NULL);
        git_config(git_trailer_config, NULL);
        configured = 1;
@@ -658,19 +674,27 @@ static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
 }
 
 static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
-                        const struct conf_info *conf)
+                        const struct conf_info *conf,
+                        const struct new_trailer_item *new_trailer_item)
 {
        struct arg_item *new = xcalloc(sizeof(*new), 1);
        new->token = tok;
        new->value = val;
        duplicate_conf(&new->conf, conf);
+       if (new_trailer_item) {
+               if (new_trailer_item->where != WHERE_DEFAULT)
+                       new->conf.where = new_trailer_item->where;
+               if (new_trailer_item->if_exists != EXISTS_DEFAULT)
+                       new->conf.if_exists = new_trailer_item->if_exists;
+               if (new_trailer_item->if_missing != MISSING_DEFAULT)
+                       new->conf.if_missing = new_trailer_item->if_missing;
+       }
        list_add_tail(&new->list, arg_head);
 }
 
 static void process_command_line_args(struct list_head *arg_head,
-                                     struct string_list *trailers)
+                                     struct list_head *new_trailer_head)
 {
-       struct string_list_item *tr;
        struct arg_item *item;
        struct strbuf tok = STRBUF_INIT;
        struct strbuf val = STRBUF_INIT;
@@ -690,26 +714,29 @@ static void process_command_line_args(struct list_head *arg_head,
                        add_arg_item(arg_head,
                                     xstrdup(token_from_item(item, NULL)),
                                     xstrdup(""),
-                                    &item->conf);
+                                    &item->conf, NULL);
        }
 
        /* Add an arg item for each trailer on the command line */
-       for_each_string_list_item(tr, trailers) {
-               int separator_pos = find_separator(tr->string, cl_separators);
+       list_for_each(pos, new_trailer_head) {
+               struct new_trailer_item *tr =
+                       list_entry(pos, struct new_trailer_item, list);
+               int separator_pos = find_separator(tr->text, cl_separators);
+
                if (separator_pos == 0) {
                        struct strbuf sb = STRBUF_INIT;
-                       strbuf_addstr(&sb, tr->string);
+                       strbuf_addstr(&sb, tr->text);
                        strbuf_trim(&sb);
                        error(_("empty trailer token in trailer '%.*s'"),
                              (int) sb.len, sb.buf);
                        strbuf_release(&sb);
                } else {
-                       parse_trailer(&tok, &val, &conf, tr->string,
+                       parse_trailer(&tok, &val, &conf, tr->text,
                                      separator_pos);
                        add_arg_item(arg_head,
                                     strbuf_detach(&tok, NULL),
                                     strbuf_detach(&val, NULL),
-                                    conf);
+                                    conf, tr);
                }
        }
 
@@ -885,9 +912,37 @@ static int ends_with_blank_line(const char *buf, size_t len)
        return is_blank_line(buf + ll);
 }
 
+static void unfold_value(struct strbuf *val)
+{
+       struct strbuf out = STRBUF_INIT;
+       size_t i;
+
+       strbuf_grow(&out, val->len);
+       i = 0;
+       while (i < val->len) {
+               char c = val->buf[i++];
+               if (c == '\n') {
+                       /* Collapse continuation down to a single space. */
+                       while (i < val->len && isspace(val->buf[i]))
+                               i++;
+                       strbuf_addch(&out, ' ');
+               } else {
+                       strbuf_addch(&out, c);
+               }
+       }
+
+       /* Empty lines may have left us with whitespace cruft at the edges */
+       strbuf_trim(&out);
+
+       /* output goes back to val as if we modified it in-place */
+       strbuf_swap(&out, val);
+       strbuf_release(&out);
+}
+
 static int process_input_file(FILE *outfile,
                              const char *str,
-                             struct list_head *head)
+                             struct list_head *head,
+                             const struct process_trailer_options *opts)
 {
        struct trailer_info info;
        struct strbuf tok = STRBUF_INIT;
@@ -897,9 +952,10 @@ static int process_input_file(FILE *outfile,
        trailer_info_get(&info, str);
 
        /* Print lines before the trailers as is */
-       fwrite(str, 1, info.trailer_start - str, outfile);
+       if (!opts->only_trailers)
+               fwrite(str, 1, info.trailer_start - str, outfile);
 
-       if (!info.blank_line_before_trailer)
+       if (!opts->only_trailers && !info.blank_line_before_trailer)
                fprintf(outfile, "\n");
 
        for (i = 0; i < info.trailer_nr; i++) {
@@ -911,10 +967,12 @@ static int process_input_file(FILE *outfile,
                if (separator_pos >= 1) {
                        parse_trailer(&tok, &val, NULL, trailer,
                                      separator_pos);
+                       if (opts->unfold)
+                               unfold_value(&val);
                        add_trailer_item(head,
                                         strbuf_detach(&tok, NULL),
                                         strbuf_detach(&val, NULL));
-               } else {
+               } else if (!opts->only_trailers) {
                        strbuf_addstr(&val, trailer);
                        strbuf_strip_suffix(&val, "\n");
                        add_trailer_item(head,
@@ -937,7 +995,7 @@ static void free_all(struct list_head *head)
        }
 }
 
-static struct tempfile trailers_tempfile;
+static struct tempfile *trailers_tempfile;
 
 static FILE *create_in_place_tempfile(const char *file)
 {
@@ -959,19 +1017,20 @@ static FILE *create_in_place_tempfile(const char *file)
                strbuf_add(&template, file, tail - file + 1);
        strbuf_addstr(&template, "git-interpret-trailers-XXXXXX");
 
-       xmks_tempfile_m(&trailers_tempfile, template.buf, st.st_mode);
+       trailers_tempfile = xmks_tempfile_m(template.buf, st.st_mode);
        strbuf_release(&template);
-       outfile = fdopen_tempfile(&trailers_tempfile, "w");
+       outfile = fdopen_tempfile(trailers_tempfile, "w");
        if (!outfile)
                die_errno(_("could not open temporary file"));
 
        return outfile;
 }
 
-void process_trailers(const char *file, int in_place, int trim_empty, struct string_list *trailers)
+void process_trailers(const char *file,
+                     const struct process_trailer_options *opts,
+                     struct list_head *new_trailer_head)
 {
        LIST_HEAD(head);
-       LIST_HEAD(arg_head);
        struct strbuf sb = STRBUF_INIT;
        int trailer_end;
        FILE *outfile = stdout;
@@ -980,24 +1039,27 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str
 
        read_input_file(&sb, file);
 
-       if (in_place)
+       if (opts->in_place)
                outfile = create_in_place_tempfile(file);
 
        /* Print the lines before the trailers */
-       trailer_end = process_input_file(outfile, sb.buf, &head);
-
-       process_command_line_args(&arg_head, trailers);
+       trailer_end = process_input_file(outfile, sb.buf, &head, opts);
 
-       process_trailers_lists(&head, &arg_head);
+       if (!opts->only_input) {
+               LIST_HEAD(arg_head);
+               process_command_line_args(&arg_head, new_trailer_head);
+               process_trailers_lists(&head, &arg_head);
+       }
 
-       print_all(outfile, &head, trim_empty);
+       print_all(outfile, &head, opts);
 
        free_all(&head);
 
        /* Print the lines after the trailers as is */
-       fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
+       if (!opts->only_trailers)
+               fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
 
-       if (in_place)
+       if (opts->in_place)
                if (rename_tempfile(&trailers_tempfile, file))
                        die_errno(_("could not rename temporary file to %s"), file);
 
@@ -1054,3 +1116,49 @@ void trailer_info_release(struct trailer_info *info)
                free(info->trailers[i]);
        free(info->trailers);
 }
+
+static void format_trailer_info(struct strbuf *out,
+                               const struct trailer_info *info,
+                               const struct process_trailer_options *opts)
+{
+       int i;
+
+       /* If we want the whole block untouched, we can take the fast path. */
+       if (!opts->only_trailers && !opts->unfold) {
+               strbuf_add(out, info->trailer_start,
+                          info->trailer_end - info->trailer_start);
+               return;
+       }
+
+       for (i = 0; i < info->trailer_nr; i++) {
+               char *trailer = info->trailers[i];
+               int separator_pos = find_separator(trailer, separators);
+
+               if (separator_pos >= 1) {
+                       struct strbuf tok = STRBUF_INIT;
+                       struct strbuf val = STRBUF_INIT;
+
+                       parse_trailer(&tok, &val, NULL, trailer, separator_pos);
+                       if (opts->unfold)
+                               unfold_value(&val);
+
+                       strbuf_addf(out, "%s: %s\n", tok.buf, val.buf);
+                       strbuf_release(&tok);
+                       strbuf_release(&val);
+
+               } else if (!opts->only_trailers) {
+                       strbuf_addstr(out, trailer);
+               }
+       }
+
+}
+
+void format_trailers_from_commit(struct strbuf *out, const char *msg,
+                                const struct process_trailer_options *opts)
+{
+       struct trailer_info info;
+
+       trailer_info_get(&info, msg);
+       format_trailer_info(out, &info, opts);
+       trailer_info_release(&info);
+}
index 65cc5d79c6cecfce5cf3302dd7dcb6323f9f85dc..6d7f8c2a52305d3d937b69a877f1ae0b7af94363 100644 (file)
--- a/trailer.h
+++ b/trailer.h
@@ -1,6 +1,33 @@
 #ifndef TRAILER_H
 #define TRAILER_H
 
+#include "list.h"
+
+enum trailer_where {
+       WHERE_DEFAULT,
+       WHERE_END,
+       WHERE_AFTER,
+       WHERE_BEFORE,
+       WHERE_START
+};
+enum trailer_if_exists {
+       EXISTS_DEFAULT,
+       EXISTS_ADD_IF_DIFFERENT_NEIGHBOR,
+       EXISTS_ADD_IF_DIFFERENT,
+       EXISTS_ADD,
+       EXISTS_REPLACE,
+       EXISTS_DO_NOTHING
+};
+enum trailer_if_missing {
+       MISSING_DEFAULT,
+       MISSING_ADD,
+       MISSING_DO_NOTHING
+};
+
+int trailer_set_where(enum trailer_where *item, const char *value);
+int trailer_set_if_exists(enum trailer_if_exists *item, const char *value);
+int trailer_set_if_missing(enum trailer_if_missing *item, const char *value);
+
 struct trailer_info {
        /*
         * True if there is a blank line before the location pointed to by
@@ -22,11 +49,50 @@ struct trailer_info {
        size_t trailer_nr;
 };
 
-void process_trailers(const char *file, int in_place, int trim_empty,
-                     struct string_list *trailers);
+/*
+ * A list that represents newly-added trailers, such as those provided
+ * with the --trailer command line option of git-interpret-trailers.
+ */
+struct new_trailer_item {
+       struct list_head list;
+
+       const char *text;
+
+       enum trailer_where where;
+       enum trailer_if_exists if_exists;
+       enum trailer_if_missing if_missing;
+};
+
+struct process_trailer_options {
+       int in_place;
+       int trim_empty;
+       int only_trailers;
+       int only_input;
+       int unfold;
+};
+
+#define PROCESS_TRAILER_OPTIONS_INIT {0}
+
+void process_trailers(const char *file,
+                     const struct process_trailer_options *opts,
+                     struct list_head *new_trailer_head);
 
 void trailer_info_get(struct trailer_info *info, const char *str);
 
 void trailer_info_release(struct trailer_info *info);
 
+/*
+ * Format the trailers from the commit msg "msg" into the strbuf "out".
+ * Note two caveats about "opts":
+ *
+ *   - this is primarily a helper for pretty.c, and not
+ *     all of the flags are supported.
+ *
+ *   - this differs from process_trailers slightly in that we always format
+ *     only the trailer block itself, even if the "only_trailers" option is not
+ *     set.
+ */
+void format_trailers_from_commit(struct strbuf *out, const char *msg,
+                                const struct process_trailer_options *opts);
+
 #endif /* TRAILER_H */
index 8f68d69a86bd919162b62514af0b1d1d55e204a1..f50b34df2dba4827db6a96cf97d1c4ac7ca811a0 100644 (file)
@@ -1117,6 +1117,13 @@ int transport_helper_init(struct transport *transport, const char *name)
 __attribute__((format (printf, 1, 2)))
 static void transfer_debug(const char *fmt, ...)
 {
+       /*
+        * NEEDSWORK: This function is sometimes used from multiple threads, and
+        * we end up using debug_enabled racily. That "should not matter" since
+        * we always write the same value, but it's still wrong. This function
+        * is listed in .tsan-suppressions for the time being.
+        */
+
        va_list args;
        char msgbuf[PBUFFERSIZE];
        static int debug_enabled = -1;
index 2357f72899f8f47e497ffd1bb66a0d76d7dbe012..4bb93155bc6e0349156b21ad1287d573593a79d5 100644 (file)
@@ -421,9 +421,8 @@ static struct combine_diff_path *ll_diff_tree_paths(
         *   diff_tree_oid(parent, commit) )
         */
        for (i = 0; i < nparent; ++i)
-               tptree[i] = fill_tree_descriptor(&tp[i],
-                               parents_oid[i] ? parents_oid[i]->hash : NULL);
-       ttree = fill_tree_descriptor(&t, oid ? oid->hash : NULL);
+               tptree[i] = fill_tree_descriptor(&tp[i], parents_oid[i]);
+       ttree = fill_tree_descriptor(&t, oid);
 
        /* Enable recursion indefinitely */
        opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
index 6a42e402b00a3da90a008adf6e4c68dc8c45e516..c99309069a90cec08b90ccab62b316a15ddd8672 100644 (file)
@@ -78,15 +78,16 @@ int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned l
        return result;
 }
 
-void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
+void *fill_tree_descriptor(struct tree_desc *desc, const struct object_id *oid)
 {
        unsigned long size = 0;
        void *buf = NULL;
 
-       if (sha1) {
-               buf = read_object_with_reference(sha1, tree_type, &size, NULL);
+       if (oid) {
+               buf = read_object_with_reference(oid->hash, tree_type, &size,
+                                                NULL);
                if (!buf)
-                       die("unable to read tree %s", sha1_to_hex(sha1));
+                       die("unable to read tree %s", oid_to_hex(oid));
        }
        init_tree_desc(desc, buf, size);
        return buf;
index 68bb78b928b5059202e5672f445fd5d6e22f9921..b6bd1b4ccfbb8bb69c464ea687c63a2058a424b8 100644 (file)
@@ -42,7 +42,7 @@ int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long
 int tree_entry(struct tree_desc *, struct name_entry *);
 int tree_entry_gently(struct tree_desc *, struct name_entry *);
 
-void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1);
+void *fill_tree_descriptor(struct tree_desc *desc, const struct object_id *oid);
 
 struct traverse_info;
 typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
index 38000ac8fad4e8cb626b74bf80197e070084246e..71b70ccb12756cde1f1c5aeda952be6e2b91f5b8 100644 (file)
@@ -1,5 +1,6 @@
 #define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
+#include "repository.h"
 #include "config.h"
 #include "dir.h"
 #include "tree.h"
@@ -162,7 +163,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
        msgs[ERROR_BIND_OVERLAP] = _("Entry '%s' overlaps with '%s'.  Cannot bind.");
 
        msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
-               _("Cannot update sparse checkout: the following entries are not up-to-date:\n%s");
+               _("Cannot update sparse checkout: the following entries are not up to date:\n%s");
        msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
                _("The following working tree files would be overwritten by sparse checkout update:\n%s");
        msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
@@ -255,47 +256,41 @@ static int check_submodule_move_head(const struct cache_entry *ce,
 {
        unsigned flags = SUBMODULE_MOVE_HEAD_DRY_RUN;
        const struct submodule *sub = submodule_from_ce(ce);
+
        if (!sub)
                return 0;
 
        if (o->reset)
                flags |= SUBMODULE_MOVE_HEAD_FORCE;
 
-       switch (sub->update_strategy.type) {
-       case SM_UPDATE_UNSPECIFIED:
-       case SM_UPDATE_CHECKOUT:
-               if (submodule_move_head(ce->name, old_id, new_id, flags))
-                       return o->gently ? -1 :
-                               add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
-               return 0;
-       case SM_UPDATE_NONE:
-               return 0;
-       case SM_UPDATE_REBASE:
-       case SM_UPDATE_MERGE:
-       case SM_UPDATE_COMMAND:
-       default:
-               warning(_("submodule update strategy not supported for submodule '%s'"), ce->name);
-               return -1;
-       }
+       if (submodule_move_head(ce->name, old_id, new_id, flags))
+               return o->gently ? -1 :
+                                  add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
+       return 0;
 }
 
-static void reload_gitmodules_file(struct index_state *index,
-                                  struct checkout *state)
+/*
+ * Preform the loading of the repository's gitmodules file.  This function is
+ * used by 'check_update()' to perform loading of the gitmodules file in two
+ * differnt situations:
+ * (1) before removing entries from the working tree if the gitmodules file has
+ *     been marked for removal.  This situation is specified by 'state' == NULL.
+ * (2) before checking out entries to the working tree if the gitmodules file
+ *     has been marked for update.  This situation is specified by 'state' != NULL.
+ */
+static void load_gitmodules_file(struct index_state *index,
+                                struct checkout *state)
 {
-       int i;
-       for (i = 0; i < index->cache_nr; i++) {
-               struct cache_entry *ce = index->cache[i];
-               if (ce->ce_flags & CE_UPDATE) {
-                       int r = strcmp(ce->name, GITMODULES_FILE);
-                       if (r < 0)
-                               continue;
-                       else if (r == 0) {
-                               submodule_free();
-                               checkout_entry(ce, state, NULL);
-                               gitmodules_config();
-                               git_config(submodule_config, NULL);
-                       } else
-                               break;
+       int pos = index_name_pos(index, GITMODULES_FILE, strlen(GITMODULES_FILE));
+
+       if (pos >= 0) {
+               struct cache_entry *ce = index->cache[pos];
+               if (!state && ce->ce_flags & CE_WT_REMOVE) {
+                       repo_read_gitmodules(the_repository);
+               } else if (state && (ce->ce_flags & CE_UPDATE)) {
+                       submodule_free();
+                       checkout_entry(ce, state, NULL);
+                       repo_read_gitmodules(the_repository);
                }
        }
 }
@@ -308,19 +303,9 @@ static void unlink_entry(const struct cache_entry *ce)
 {
        const struct submodule *sub = submodule_from_ce(ce);
        if (sub) {
-               switch (sub->update_strategy.type) {
-               case SM_UPDATE_UNSPECIFIED:
-               case SM_UPDATE_CHECKOUT:
-               case SM_UPDATE_REBASE:
-               case SM_UPDATE_MERGE:
-                       /* state.force is set at the caller. */
-                       submodule_move_head(ce->name, "HEAD", NULL,
-                                           SUBMODULE_MOVE_HEAD_FORCE);
-                       break;
-               case SM_UPDATE_NONE:
-               case SM_UPDATE_COMMAND:
-                       return; /* Do not touch the submodule. */
-               }
+               /* state.force is set at the caller. */
+               submodule_move_head(ce->name, "HEAD", NULL,
+                                   SUBMODULE_MOVE_HEAD_FORCE);
        }
        if (!check_leading_path(ce->name, ce_namelen(ce)))
                return;
@@ -343,8 +328,7 @@ static struct progress *get_progress(struct unpack_trees_options *o)
                        total++;
        }
 
-       return start_progress_delay(_("Checking out files"),
-                                   total, 50, 1);
+       return start_delayed_progress(_("Checking out files"), total);
 }
 
 static int check_updates(struct unpack_trees_options *o)
@@ -365,6 +349,10 @@ static int check_updates(struct unpack_trees_options *o)
 
        if (o->update)
                git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
+
+       if (should_update_submodules() && o->update && !o->dry_run)
+               load_gitmodules_file(index, NULL);
+
        for (i = 0; i < index->cache_nr; i++) {
                const struct cache_entry *ce = index->cache[i];
 
@@ -378,7 +366,7 @@ static int check_updates(struct unpack_trees_options *o)
        remove_scheduled_dirs();
 
        if (should_update_submodules() && o->update && !o->dry_run)
-               reload_gitmodules_file(index, &state);
+               load_gitmodules_file(index, &state);
 
        enable_delayed_checkout(&state);
        for (i = 0; i < index->cache_nr; i++) {
@@ -395,8 +383,8 @@ static int check_updates(struct unpack_trees_options *o)
                        }
                }
        }
-       errs |= finish_delayed_checkout(&state);
        stop_progress(&progress);
+       errs |= finish_delayed_checkout(&state);
        if (o->update)
                git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
        return errs != 0;
@@ -662,10 +650,10 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
                else if (i > 1 && are_same_oid(&names[i], &names[i - 2]))
                        t[i] = t[i - 2];
                else {
-                       const unsigned char *sha1 = NULL;
+                       const struct object_id *oid = NULL;
                        if (dirmask & 1)
-                               sha1 = names[i].oid->hash;
-                       buf[nr_buf++] = fill_tree_descriptor(t+i, sha1);
+                               oid = names[i].oid;
+                       buf[nr_buf++] = fill_tree_descriptor(t + i, oid);
                }
        }
 
index 5a89db30e3ffcb64ee25597fe014592929e07abb..3fd047a8b82b1c2a6b32ec0a5ded3d67913b5be0 100644 (file)
@@ -6,7 +6,6 @@
 #include "cache.h"
 #include "quote.h"
 #include "fast_export.h"
-#include "repo_tree.h"
 #include "strbuf.h"
 #include "svndiff.h"
 #include "sliding_window.h"
@@ -210,7 +209,7 @@ static long apply_delta(off_t len, struct line_buffer *input,
                        die("invalid cat-blob response: %s", response);
                check_preimage_overflow(preimage.max_off, 1);
        }
-       if (old_mode == REPO_MODE_LNK) {
+       if (old_mode == S_IFLNK) {
                strbuf_addstr(&preimage.buf, "link ");
                check_preimage_overflow(preimage.max_off, strlen("link "));
                preimage.max_off += strlen("link ");
@@ -244,7 +243,7 @@ void fast_export_buf_to_data(const struct strbuf *data)
 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input)
 {
        assert(len >= 0);
-       if (mode == REPO_MODE_LNK) {
+       if (mode == S_IFLNK) {
                /* svn symlink blobs start with "link " */
                if (len < 5)
                        die("invalid dump: symlink too short for \"link\" prefix");
@@ -312,6 +311,40 @@ int fast_export_ls(const char *path, uint32_t *mode, struct strbuf *dataref)
        return parse_ls_response(get_response_line(), mode, dataref);
 }
 
+const char *fast_export_read_path(const char *path, uint32_t *mode_out)
+{
+       int err;
+       static struct strbuf buf = STRBUF_INIT;
+
+       strbuf_reset(&buf);
+       err = fast_export_ls(path, mode_out, &buf);
+       if (err) {
+               if (errno != ENOENT)
+                       die_errno("BUG: unexpected fast_export_ls error");
+               /* Treat missing paths as directories. */
+               *mode_out = S_IFDIR;
+               return NULL;
+       }
+       return buf.buf;
+}
+
+void fast_export_copy(uint32_t revision, const char *src, const char *dst)
+{
+       int err;
+       uint32_t mode;
+       static struct strbuf data = STRBUF_INIT;
+
+       strbuf_reset(&data);
+       err = fast_export_ls_rev(revision, src, &mode, &data);
+       if (err) {
+               if (errno != ENOENT)
+                       die_errno("BUG: unexpected fast_export_ls_rev error");
+               fast_export_delete(dst);
+               return;
+       }
+       fast_export_modify(dst, mode, data.buf);
+}
+
 void fast_export_blob_delta(uint32_t mode,
                                uint32_t old_mode, const char *old_data,
                                off_t len, struct line_buffer *input)
@@ -320,7 +353,7 @@ void fast_export_blob_delta(uint32_t mode,
 
        assert(len >= 0);
        postimage_len = apply_delta(len, input, old_data, old_mode);
-       if (mode == REPO_MODE_LNK) {
+       if (mode == S_IFLNK) {
                buffer_skip_bytes(&postimage, strlen("link "));
                postimage_len -= strlen("link ");
        }
index b9a3b71c99fd59312f493d6b1eb24c9a0b44578c..60b79c35b97cbdc9589fe3778ac1f4b5f61646ff 100644 (file)
@@ -28,4 +28,7 @@ int fast_export_ls_rev(uint32_t rev, const char *path,
 int fast_export_ls(const char *path,
                        uint32_t *mode_out, struct strbuf *dataref_out);
 
+void fast_export_copy(uint32_t revision, const char *src, const char *dst);
+const char *fast_export_read_path(const char *path, uint32_t *mode_out);
+
 #endif
diff --git a/vcs-svn/repo_tree.c b/vcs-svn/repo_tree.c
deleted file mode 100644 (file)
index 67d27f0..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed under a two-clause BSD-style license.
- * See LICENSE for details.
- */
-
-#include "git-compat-util.h"
-#include "strbuf.h"
-#include "repo_tree.h"
-#include "fast_export.h"
-
-const char *repo_read_path(const char *path, uint32_t *mode_out)
-{
-       int err;
-       static struct strbuf buf = STRBUF_INIT;
-
-       strbuf_reset(&buf);
-       err = fast_export_ls(path, mode_out, &buf);
-       if (err) {
-               if (errno != ENOENT)
-                       die_errno("BUG: unexpected fast_export_ls error");
-               /* Treat missing paths as directories. */
-               *mode_out = REPO_MODE_DIR;
-               return NULL;
-       }
-       return buf.buf;
-}
-
-void repo_copy(uint32_t revision, const char *src, const char *dst)
-{
-       int err;
-       uint32_t mode;
-       static struct strbuf data = STRBUF_INIT;
-
-       strbuf_reset(&data);
-       err = fast_export_ls_rev(revision, src, &mode, &data);
-       if (err) {
-               if (errno != ENOENT)
-                       die_errno("BUG: unexpected fast_export_ls_rev error");
-               fast_export_delete(dst);
-               return;
-       }
-       fast_export_modify(dst, mode, data.buf);
-}
-
-void repo_delete(const char *path)
-{
-       fast_export_delete(path);
-}
diff --git a/vcs-svn/repo_tree.h b/vcs-svn/repo_tree.h
deleted file mode 100644 (file)
index 889c6a3..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef REPO_TREE_H_
-#define REPO_TREE_H_
-
-struct strbuf;
-
-#define REPO_MODE_DIR 0040000
-#define REPO_MODE_BLB 0100644
-#define REPO_MODE_EXE 0100755
-#define REPO_MODE_LNK 0120000
-
-uint32_t next_blob_mark(void);
-void repo_copy(uint32_t revision, const char *src, const char *dst);
-void repo_add(const char *path, uint32_t mode, uint32_t blob_mark);
-const char *repo_read_path(const char *path, uint32_t *mode_out);
-void repo_delete(const char *path);
-void repo_commit(uint32_t revision, const char *author,
-               const struct strbuf *log, const char *uuid, const char *url,
-               long unsigned timestamp);
-void repo_diff(uint32_t r1, uint32_t r2);
-void repo_init(void);
-void repo_reset(void);
-
-#endif
index 1846685a21a44decfe5790f0020dcf933c8d6583..ec6b350611d367fd75f909c4695d9faf2d68f91e 100644 (file)
@@ -8,7 +8,6 @@
  */
 
 #include "cache.h"
-#include "repo_tree.h"
 #include "fast_export.h"
 #include "line_buffer.h"
 #include "strbuf.h"
@@ -134,13 +133,13 @@ static void handle_property(const struct strbuf *key_buf,
                        die("invalid dump: sets type twice");
                }
                if (!val) {
-                       node_ctx.type = REPO_MODE_BLB;
+                       node_ctx.type = S_IFREG | 0644;
                        return;
                }
                *type_set = 1;
                node_ctx.type = keylen == strlen("svn:executable") ?
-                               REPO_MODE_EXE :
-                               REPO_MODE_LNK;
+                               (S_IFREG | 0755) :
+                               S_IFLNK;
        }
 }
 
@@ -219,45 +218,45 @@ static void handle_node(void)
         */
        static const char *const empty_blob = "::empty::";
        const char *old_data = NULL;
-       uint32_t old_mode = REPO_MODE_BLB;
+       uint32_t old_mode = S_IFREG | 0644;
 
        if (node_ctx.action == NODEACT_DELETE) {
                if (have_text || have_props || node_ctx.srcRev)
                        die("invalid dump: deletion node has "
                                "copyfrom info, text, or properties");
-               repo_delete(node_ctx.dst.buf);
+               fast_export_delete(node_ctx.dst.buf);
                return;
        }
        if (node_ctx.action == NODEACT_REPLACE) {
-               repo_delete(node_ctx.dst.buf);
+               fast_export_delete(node_ctx.dst.buf);
                node_ctx.action = NODEACT_ADD;
        }
        if (node_ctx.srcRev) {
-               repo_copy(node_ctx.srcRev, node_ctx.src.buf, node_ctx.dst.buf);
+               fast_export_copy(node_ctx.srcRev, node_ctx.src.buf, node_ctx.dst.buf);
                if (node_ctx.action == NODEACT_ADD)
                        node_ctx.action = NODEACT_CHANGE;
        }
-       if (have_text && type == REPO_MODE_DIR)
+       if (have_text && type == S_IFDIR)
                die("invalid dump: directories cannot have text attached");
 
        /*
         * Find old content (old_data) and decide on the new mode.
         */
        if (node_ctx.action == NODEACT_CHANGE && !*node_ctx.dst.buf) {
-               if (type != REPO_MODE_DIR)
+               if (type != S_IFDIR)
                        die("invalid dump: root of tree is not a regular file");
                old_data = NULL;
        } else if (node_ctx.action == NODEACT_CHANGE) {
                uint32_t mode;
-               old_data = repo_read_path(node_ctx.dst.buf, &mode);
-               if (mode == REPO_MODE_DIR && type != REPO_MODE_DIR)
+               old_data = fast_export_read_path(node_ctx.dst.buf, &mode);
+               if (mode == S_IFDIR && type != S_IFDIR)
                        die("invalid dump: cannot modify a directory into a file");
-               if (mode != REPO_MODE_DIR && type == REPO_MODE_DIR)
+               if (mode != S_IFDIR && type == S_IFDIR)
                        die("invalid dump: cannot modify a file into a directory");
                node_ctx.type = mode;
                old_mode = mode;
        } else if (node_ctx.action == NODEACT_ADD) {
-               if (type == REPO_MODE_DIR)
+               if (type == S_IFDIR)
                        old_data = NULL;
                else if (have_text)
                        old_data = empty_blob;
@@ -280,7 +279,7 @@ static void handle_node(void)
        /*
         * Save the result.
         */
-       if (type == REPO_MODE_DIR)      /* directories are not tracked. */
+       if (type == S_IFDIR)    /* directories are not tracked. */
                return;
        assert(old_data);
        if (old_data == empty_blob)
@@ -385,9 +384,9 @@ void svndump_read(const char *url, const char *local_ref, const char *notes_ref)
                                continue;
                        strbuf_addf(&rev_ctx.note, "%s\n", t);
                        if (!strcmp(val, "dir"))
-                               node_ctx.type = REPO_MODE_DIR;
+                               node_ctx.type = S_IFDIR;
                        else if (!strcmp(val, "file"))
-                               node_ctx.type = REPO_MODE_BLB;
+                               node_ctx.type = S_IFREG | 0644;
                        else
                                fprintf(stderr, "Unknown node-kind: %s\n", val);
                        break;
index e28ffbeb096a85d67b2e885c48ab096340b5d295..8aaeea03771cfefa9edd019d0dc5146fef17a9c7 100644 (file)
@@ -30,7 +30,7 @@ static void add_head_info(struct worktree *wt)
 
        target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
                                         "HEAD",
-                                        RESOLVE_REF_READING,
+                                        0,
                                         wt->head_sha1, &flags);
        if (!target)
                return;
@@ -386,3 +386,25 @@ int submodule_uses_worktrees(const char *path)
        closedir(dir);
        return ret;
 }
+
+int other_head_refs(each_ref_fn fn, void *cb_data)
+{
+       struct worktree **worktrees, **p;
+       int ret = 0;
+
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+               struct ref_store *refs;
+
+               if (wt->is_current)
+                       continue;
+
+               refs = get_worktree_ref_store(wt);
+               ret = refs_head_ref(refs, fn, cb_data);
+               if (ret)
+                       break;
+       }
+       free_worktrees(worktrees);
+       return ret;
+}
index 5ea5e503fbe491a76d3c8ce279b7e281a5d07d3e..9276c81ae7cb5c42a86fd9ebbdb28720be409e47 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef WORKTREE_H
 #define WORKTREE_H
 
+#include "refs.h"
+
 struct worktree {
        char *path;
        char *id;
@@ -70,6 +72,12 @@ extern void free_worktrees(struct worktree **);
 extern const struct worktree *find_shared_symref(const char *symref,
                                                 const char *target);
 
+/*
+ * Similar to head_ref() for all HEADs _except_ one from the current
+ * worktree, which is covered by head_ref().
+ */
+int other_head_refs(each_ref_fn fn, void *cb_data);
+
 int is_worktree_being_rebased(const struct worktree *wt, const char *target);
 int is_worktree_being_bisected(const struct worktree *wt, const char *target);