Sync with v2.9.1
authorJunio C Hamano <gitster@pobox.com>
Mon, 11 Jul 2016 17:46:39 +0000 (10:46 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 11 Jul 2016 17:46:39 +0000 (10:46 -0700)
* maint:
Git 2.9.1

111 files changed:
Documentation/RelNotes/2.10.0.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-am.txt
Documentation/git-fast-import.txt
Documentation/git-log.txt
Documentation/git-mailsplit.txt
Documentation/git-repack.txt
Documentation/git-submodule.txt
Documentation/git-upload-pack.txt
Documentation/git-worktree.txt
Documentation/git.txt
Documentation/gitattributes.txt
GIT-VERSION-GEN
RelNotes
archive.c
builtin/am.c
builtin/apply.c
builtin/blame.c
builtin/clone.c
builtin/commit.c
builtin/interpret-trailers.c
builtin/log.c
builtin/mailsplit.c
builtin/pack-objects.c
builtin/pull.c
builtin/receive-pack.c
builtin/remote.c
builtin/repack.c
builtin/reset.c
builtin/shortlog.c
builtin/submodule--helper.c
builtin/update-index.c
builtin/worktree.c
cache.h
color.c
color.h
combine-diff.c
commit.c
commit.h
config.c
contrib/completion/git-completion.bash
fast-import.c
git-compat-util.h
git-send-email.perl
git-submodule.sh
git-svn.perl
gpg-interface.c
http.c
http.h
imap-send.c
log-tree.c
notes-merge.c
notes.c
parse-options-cb.c
pathspec.c
pathspec.h
perl/Git/SVN.pm
pretty.c
revision.c
run-command.c
run-command.h
send-pack.c
sequencer.c
sideband.c
sideband.h
submodule-config.c
submodule-config.h
submodule.c
t/helper/test-config.c
t/helper/test-parse-options.c
t/lib-httpd.sh
t/perf/README
t/perf/p4211-line-log.sh
t/perf/perf-lib.sh
t/t1308-config-set.sh
t/t2025-worktree-add.sh
t/t4014-format-patch.sh
t/t4018-diff-funcname.sh
t/t4018/css-brace-in-col-1 [new file with mode: 0644]
t/t4018/css-colon-eol [new file with mode: 0644]
t/t4018/css-colon-selector [new file with mode: 0644]
t/t4018/css-common [new file with mode: 0644]
t/t4018/css-long-selector-list [new file with mode: 0644]
t/t4018/css-prop-sans-indent [new file with mode: 0644]
t/t4018/css-short-selector-list [new file with mode: 0644]
t/t4018/css-trailing-space [new file with mode: 0644]
t/t4026-color.sh
t/t4034-diff-words.sh
t/t4034/css/expect [new file with mode: 0644]
t/t4034/css/post [new file with mode: 0644]
t/t4034/css/pre [new file with mode: 0644]
t/t4150-am.sh
t/t4202-log.sh
t/t5100-mailinfo.sh
t/t5100/0001mboxrd [new file with mode: 0644]
t/t5100/0002mboxrd [new file with mode: 0644]
t/t5100/sample.mboxrd [new file with mode: 0644]
t/t5520-pull.sh
t/t5544-pack-objects-hook.sh [new file with mode: 0755]
t/t5614-clone-submodules.sh
t/t7004-tag.sh
t/t7510-signed-commit.sh
t/t7701-repack-unpack-unreachable.sh
t/t8008-blame-formats.sh
t/t9300-fast-import.sh
t/t9302-fast-import-unpack-limit.sh [new file with mode: 0755]
tree-diff.c
upload-pack.c
userdiff.c
worktree.c
write_or_die.c
diff --git a/Documentation/RelNotes/2.10.0.txt b/Documentation/RelNotes/2.10.0.txt
new file mode 100644 (file)
index 0000000..4783e25
--- /dev/null
@@ -0,0 +1,268 @@
+Git 2.10 Release Notes
+======================
+
+Backward compatibility notes
+----------------------------
+
+Updates since v2.9
+------------------
+
+UI, Workflows & Features
+
+ * "git pull --rebase --verify-signature" learned to warn the user
+   that "--verify-signature" is a no-op when rebasing.
+
+ * An upstream project can make a recommendation to shallowly clone
+   some submodules in the .gitmodules file it ships.
+
+ * "git worktree add" learned that '-' can be used as a short-hand for
+   "@{-1}", the previous branch.
+
+ * Update the funcname definition to support css files.
+
+ * The completion script (in contrib/) learned to complete "git
+   status" options.
+
+ * Messages that are generated by auto gc during "git push" on the
+   receiving end are now passed back to the sending end in such a way
+   that they are shown with "remote: " prefix to avoid confusing the
+   users.
+
+ * "git add -i/-p" learned to honor diff.compactionHeuristic
+   experimental knob, so that the user can work on the same hunk split
+   as "git diff" output.
+   (merge 46e3d17 jk/add-i-diff-compact-heuristics later to maint).
+
+ * "upload-pack" allows a custom "git pack-objects" replacement when
+   responding to "fetch/clone" via the uploadpack.packObjectsHook.
+   (merge 20b20a2 jk/upload-pack-hook later to maint).
+
+ * Teach format-patch and mailsplit (hence "am") how a line that
+   happens to begin with "From " in the e-mail message is quoted with
+   ">", so that these lines can be restored to their original shape.
+   (merge d9925d1 ew/mboxrd-format-am later to maint).
+
+ * "git repack" learned the "--keep-unreachable" option, which sends
+   loose unreachable objects to a pack instead of leaving them loose.
+   This helps heuristics based on the number of loose objects
+   (e.g. "gc --auto").
+   (merge e26a8c4 jk/repack-keep-unreachable later to maint).
+
+ * "log --graph --format=" learned that "%>|(N)" specifies the width
+   relative to the terminal's left edge, not relative to the area to
+   draw text that is to the right of the ancestry-graph section.  It
+   also now accepts negative N that means the column limit is relative
+   to the right border.
+   (merge 066790d nd/graph-width-padded later to maint).
+
+ * A careless invocation of "git send-email directory/" after editing
+   0001-change.patch with an editor often ends up sending both
+   0001-change.patch and its backup file, 0001-change.patch~, causing
+   embarrassment and a minor confusion.  Detect such an input and
+   offer to skip the backup files when sending the patches out.
+   (merge 531220b jc/send-email-skip-backup later to maint).
+
+ * "git submodule update" that drives many "git clone" could
+   eventually hit flaky servers/network conditions on one of the
+   submodules; the command learned to retry the attempt.
+
+ * The output coloring scheme learned two new attributes, italic and
+   strike, in addition to existing bold, reverse, etc.
+
+ * "git log" learns log.showSignature configuration variable, and a
+   command line option "--no-show-signature" to countermand it.
+   (merge fce04c3 mj/log-show-signature-conf later to maint).
+
+ * A couple of "git svn" updates.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * "git fast-import" learned the same performance trick to avoid
+   creating too small a packfile as "git fetch" and "git push" have,
+   using *.unpackLimit configuration.
+
+ * When "git daemon" is run without --[init-]timeout specified, a
+   connection from a client that silently goes offline can hang around
+   for a long time, wasting resources.  The socket-level KEEPALIVE has
+   been enabled to allow the OS to notice such failed connections.
+   (merge a43b68a ew/daemon-socket-keepalive later to maint).
+
+ * "git upload-pack" command has been updated to use the parse-options
+   API.
+
+ * The "git apply" standalone program is being libified; this is the
+   first step to move many state variables into a structure that can
+   be explicitly (re)initialized to make the machinery callable more
+   than once.
+
+ * HTTP transport gained an option to produce more detailed debugging
+   trace.
+   (merge 73e57aa ep/http-curl-trace later to maint).
+
+ * Instead of taking advantage of a struct string_list that is
+   allocated with all NULs happens to be STRING_LIST_INIT_NODUP kind,
+   initialize them explicitly as such, to document their behaviour
+   better.
+   (merge 2721ce2 jk/string-list-static-init later to maint).
+
+ * HTTPd tests learned to show the server error log to help diagnosing
+   a failing tests.
+   (merge 44f243d nd/test-lib-httpd-show-error-log-in-verbose later to maint).
+
+ * The ownership rule for the piece of memory that hold references to
+   be fetched in "git fetch" was screwy, which has been cleaned up.
+   (merge b7410f6 km/fetch-do-not-free-remote-name later to maint).
+
+ * "git bisect" makes an internal call to "git diff-tree" when
+   bisection finds the culprit, but this call did not initialize the
+   data structure to pass to the diff-tree API correctly.
+   (merge 43ec550 jk/bisect-show-tree later to maint).
+
+ * Further preparatory clean-up for "worktree" feature continues.
+   (merge 0409e0b nd/worktree-cleanup-post-head-protection later to maint).
+
+ * Formats of the various data (and how to validate them) where we use
+   GPG signature have been documented.
+   (merge cc6ee97 mg/signature-doc later to maint).
+
+ * A new run-command API function pipe_command() is introduced to
+   sanely feed data to the standard input while capturing data from
+   the standard output and the standard error of an external process,
+   which is cumbersome to hand-roll correctly without deadlocking.
+
+   The codepath to sign data in a prepared buffer with GPG has been
+   updated to use this API to read from the status-fd to check for
+   errors (instead of relying on GPG's exit status).
+   (merge efee955 jk/gpg-interface-cleanup later to maint).
+
+ * Allow t/perf framework to use the features from the most recent
+   version of Git even when testing an older installed version.
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.9
+----------------
+
+Unless otherwise noted, all the fixes since v2.8 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * The commands in `git log` family take %C(auto) in a custom format
+   string.  This unconditionally turned the color on, ignoring
+   --no-color or with --color=auto when the output is not connected to
+   a tty; this was corrected to make the format truly behave as
+   "auto".
+   (merge b15a3e0 et/pretty-format-c-auto later to maint).
+
+ * "git rev-list --count" whose walk-length is limited with "-n"
+   option did not work well with the counting optimized to look at the
+   bitmap index.
+   (merge fb85db8 jk/rev-list-count-with-bitmap later to maint).
+
+ * "git show -W" (extend hunks to cover the entire function, delimited
+   by lines that match the "funcname" pattern) used to show the entire
+   file when a change added an entire function at the end of the file,
+   which has been fixed.
+   (merge 6f8d9bc rs/xdiff-hunk-with-func-line later to maint).
+
+ * The documentation set has been updated so that literal commands,
+   configuration variables and environment variables are consistently
+   typeset in fixed-width font and bold in manpages.
+
+ * "git svn propset" subcommand that was added in 2.3 days is
+   documented now.
+
+ * The documentation tries to consistently spell "GPG"; when
+   referring to the specific program name, "gpg" is used.
+
+ * "git reflog" stopped upon seeing an entry that denotes a branch
+   creation event (aka "unborn"), which made it appear as if the
+   reflog was truncated.
+
+ * The git-prompt scriptlet (in contrib/) was not friendly with those
+   who uses "set -u", which has been fixed.
+
+ * compat/regex code did not cleanly compile.
+
+ * A codepath that used alloca(3) to place an unbounded amount of data
+   on the stack has been updated to avoid doing so.
+
+ * "git update-index --add --chmod=+x file" may be usable as an escape
+   hatch, but not a friendly thing to force for people who do need to
+   use it regularly.  "git add --chmod=+x file" can be used instead.
+
+ * Build improvements for gnome-keyring (in contrib/)
+
+ * "git status" used to say "working directory" when it meant "working
+   tree".
+
+ * Comments about misbehaving FreeBSD shells have been clarified with
+   the version number (9.x and before are broken, newer ones are OK).
+
+ * "git cherry-pick A" worked on an unborn branch, but "git
+   cherry-pick A..B" didn't.
+
+ * Fix an unintended regression in v2.9 that breaks "clone --depth"
+   that recurses down to submodules by forcing the submodules to also
+   be cloned shallowly, which many server instances that host upstream
+   of the submodules are not prepared for.
+   (merge 18a74a0 sb/clone-shallow-passthru later to maint).
+
+ * Fix unnecessarily waste in the idiomatic use of ': ${VAR=default}'
+   to set the default value, without enclosing it in double quotes.
+   (merge 01247e0 lc/shell-default-value-noexpand later to maint).
+
+ * Some platform-specific code had non-ANSI strict declarations of C
+   functions that do not take any parameters, which has been
+   corrected.
+   (merge 0767172 js/mingw-parameter-less-c-functions later to maint).
+
+ * The internal code used to show local timezone offset is not
+   prepared to handle timestamps beyond year 2100, and gave a
+   bogus offset value to the caller.  Use a more benign looking
+   +0000 instead and let "git log" going in such a case, instead
+   of aborting.
+   (merge bab7483 jk/tzoffset-fix later to maint).
+
+ * One among four invocations of readlink(1) in our test suite has
+   been rewritten so that the test can run on systems without the
+   command (others are in valgrind test framework and t9802).
+   (merge d2addc3 ak/t7800-wo-readlink later to maint).
+
+ * t/perf needs /usr/bin/time with GNU extension; the invocation of it
+   is updated to "gtime" on Darwin.
+   (merge e3efa94 js/perf-on-apple later to maint).
+
+ * A bug, which caused "git p4" while running under verbose mode to
+   report paths that are omitted due to branch prefix incorrectly, has
+   been fixed; the command said "Ignoring file outside of prefix" for
+   paths that are _inside_.
+   (merge 09667d0 ao/p4-has-branch-prefix-fix later to maint).
+
+ * The top level documentation "git help git" still pointed at the
+   documentation set hosted at now-defunct google-code repository.
+   Update it to point to https://git.github.io/htmldocs/git.html
+   instead.
+   (merge f793582 jn/preformatted-doc-url later to maint).
+
+ * A helper function that takes the contents of a commit object and
+   finds its subject line did not ignore leading blank lines, as is
+   commonly done by other codepaths.  Make it ignore leading blank
+   lines to match.
+   (merge 054a5ae js/find-commit-subject-ignore-leading-blanks later to maint).
+
+ * For a long time, we carried an in-code comment that said our
+   colored output would work only when we use fprintf/fputs on
+   Windows, which no longer is the case for the past few years.
+   (merge 3d0a833 js/color-on-windows-comment later to maint).
+
+ * Other minor clean-ups and documentation updates
+   (merge e51b0df pb/commit-editmsg-path later to maint).
+   (merge b333d0d jk/send-pack-stdio later to maint).
+   (merge fcf0fe9 lf/sideband-returns-void later to maint).
+   (merge 5819c2e sb/t5614-modernize later to maint).
+   (merge fe0537a cb/t7810-test-label-fix later to maint).
+   (merge 412b9a1 jc/t2300-setup later to maint).
index 4f41840fdc0fed4ad5cf469032c7f76811dd61f0..db05dec743d1ad9b6b2b4272417dc968f2d56f7f 100644 (file)
@@ -150,27 +150,34 @@ integer::
        1024", "by 1024x1024", etc.
 
 color::
-       The value for a variables that takes a color is a list of
-       colors (at most two) and attributes (at most one), separated
-       by spaces.  The colors accepted are `normal`, `black`,
-       `red`, `green`, `yellow`, `blue`, `magenta`, `cyan` and
-       `white`; the attributes are `bold`, `dim`, `ul`, `blink` and
-       `reverse`.  The first color given is the foreground; the
-       second is the background.  The position of the attribute, if
-       any, doesn't matter. Attributes may be turned off specifically
-       by prefixing them with `no` (e.g., `noreverse`, `noul`, etc).
-+
-Colors (foreground and background) may also be given as numbers between
-0 and 255; these use ANSI 256-color mode (but note that not all
-terminals may support this).  If your terminal supports it, you may also
-specify 24-bit RGB values as hex, like `#ff0ab3`.
-+
-The attributes are meant to be reset at the beginning of each item
-in the colored output, so setting color.decorate.branch to `black`
-will paint that branch name in a plain `black`, even if the previous
-thing on the same output line (e.g. opening parenthesis before the
-list of branch names in `log --decorate` output) is set to be
-painted with `bold` or some other attribute.
+       The value for a variable that takes a color is a list of
+       colors (at most two, one for foreground and one for background)
+       and attributes (as many as you want), separated by spaces.
++
+The basic colors accepted are `normal`, `black`, `red`, `green`, `yellow`,
+`blue`, `magenta`, `cyan` and `white`.  The first color given is the
+foreground; the second is the background.
++
+Colors may also be given as numbers between 0 and 255; these use ANSI
+256-color mode (but note that not all terminals may support this).  If
+your terminal supports it, you may also specify 24-bit RGB values as
+hex, like `#ff0ab3`.
++
+The accepted attributes are `bold`, `dim`, `ul`, `blink`, `reverse`,
+`italic`, and `strike` (for crossed-out or "strikethrough" letters).
+The position of any attributes with respect to the colors
+(before, after, or in between), doesn't matter. Specific attributes may
+be turned off by prefixing them with `no` or `no-` (e.g., `noreverse`,
+`no-ul`, etc).
++
+For git's pre-defined color slots, the attributes are meant to be reset
+at the beginning of each item in the colored output. So setting
+`color.decorate.branch` to `black` will paint that branch name in a
+plain `black`, even if the previous thing on the same output line (e.g.
+opening parenthesis before the list of branch names in `log --decorate`
+output) is set to be painted with `bold` or some other attribute.
+However, custom log formats may do more complicated and layered
+coloring, and the negated forms may be useful there.
 
 pathname::
        A variable that takes a pathname value can be given a
@@ -1189,6 +1196,15 @@ difftool.<tool>.cmd::
 difftool.prompt::
        Prompt before each invocation of the diff tool.
 
+fastimport.unpackLimit::
+       If the number of objects imported by linkgit:git-fast-import[1]
+       is below this limit, then the objects will be unpacked into
+       loose object files.  However if the number of imported objects
+       equals or exceeds this limit then the pack will be stored as a
+       pack.  Storing the pack from a fast-import can make the import
+       operation complete faster, especially on slow filesystems.  If
+       not set, the value of `transfer.unpackLimit` is used instead.
+
 fetch.recurseSubmodules::
        This option can be either set to a boolean value or to 'on-demand'.
        Setting it to a boolean changes the behavior of fetch and pull to
@@ -2883,6 +2899,21 @@ uploadpack.keepAlive::
        `uploadpack.keepAlive` seconds. Setting this option to 0
        disables keepalive packets entirely. The default is 5 seconds.
 
+uploadpack.packObjectsHook::
+       If this option is set, when `upload-pack` would run
+       `git pack-objects` to create a packfile for a client, it will
+       run this shell command instead.  The `pack-objects` command and
+       arguments it _would_ have run (including the `git pack-objects`
+       at the beginning) are appended to the shell command. The stdin
+       and stdout of the hook are treated as if `pack-objects` itself
+       was run. I.e., `upload-pack` will feed input intended for
+       `pack-objects` to the hook, and expects a completed packfile on
+       stdout.
++
+Note that this configuration variable is ignored if it is seen in the
+repository-level config (this is a safety measure against fetching from
+untrusted repositories).
+
 url.<base>.insteadOf::
        Any URL that starts with this value will be rewritten to
        start, instead, with <base>. In cases where some site serves a
index 13cdd7f3b636dfadc262dcf099653775ec53cd5a..6348c29feab7b67bfc58b6a34e338c58c3f88fad 100644 (file)
@@ -116,7 +116,8 @@ default.   You can use `--no-utf8` to override this.
        By default the command will try to detect the patch format
        automatically. This option allows the user to bypass the automatic
        detection and specify the patch format that the patch(es) should be
-       interpreted as. Valid formats are mbox, stgit, stgit-series and hg.
+       interpreted as. Valid formats are mbox, mboxrd,
+       stgit, stgit-series and hg.
 
 -i::
 --interactive::
index 66910aa2faff1d78b71c5c552550a4f8e9d51832..644df993f924d3265db10af6e6c29e33daf95482 100644 (file)
@@ -136,6 +136,8 @@ Performance and Compression Tuning
        Maximum size of each output packfile.
        The default is unlimited.
 
+fastimport.unpackLimit::
+       See linkgit:git-config[1]
 
 Performance
 -----------
index 4a6c47f843a8c0ca0b97a7b30648f7ca37e1f3c4..32246fdb007e9b25f414cff092b9bf0426c55d15 100644 (file)
@@ -198,6 +198,10 @@ log.showRoot::
        `git log -p` output would be shown without a diff attached.
        The default is `true`.
 
+log.showSignature::
+       If `true`, `git log` and related commands will act as if the
+       `--show-signature` option was passed to them.
+
 mailmap.*::
        See linkgit:git-shortlog[1].
 
index 4d1b871d96177ca36e7fadd115685316292dc727..e3b2a88c4b75f1f6e23feb8a09030469468d9ca8 100644 (file)
@@ -8,7 +8,8 @@ git-mailsplit - Simple UNIX mbox splitter program
 SYNOPSIS
 --------
 [verse]
-'git mailsplit' [-b] [-f<nn>] [-d<prec>] [--keep-cr] -o<directory> [--] [(<mbox>|<Maildir>)...]
+'git mailsplit' [-b] [-f<nn>] [-d<prec>] [--keep-cr] [--mboxrd]
+               -o<directory> [--] [(<mbox>|<Maildir>)...]
 
 DESCRIPTION
 -----------
@@ -47,6 +48,10 @@ OPTIONS
 --keep-cr::
        Do not remove `\r` from lines ending with `\r\n`.
 
+--mboxrd::
+       Input is of the "mboxrd" format and "^>+From " line escaping is
+       reversed.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index b9c02ce48134dd44b05053275ec4b219f3999091..b58b6b597245947898712b6f671ec316c2080275 100644 (file)
@@ -128,6 +128,19 @@ other objects in that pack they already have locally.
        with `-b` or `repack.writeBitmaps`, as it ensures that the
        bitmapped packfile has the necessary objects.
 
+--unpack-unreachable=<when>::
+       When loosening unreachable objects, do not bother loosening any
+       objects older than `<when>`. This can be used to optimize out
+       the write of any objects that would be immediately pruned by
+       a follow-up `git prune`.
+
+-k::
+--keep-unreachable::
+       When used with `-ad`, any unreachable objects from existing
+       packs will be appended to the end of the packfile instead of
+       being removed. In addition, any unreachable loose objects will
+       be packed (and their loose counterparts removed).
+
 Configuration
 -------------
 
index 9226c4380c6c147108af9d38d7f7101c732a2095..bf3bb372ee51397b362266d275cd557781585861 100644 (file)
@@ -15,8 +15,9 @@ SYNOPSIS
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] deinit [-f|--force] (--all|[--] <path>...)
 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
-             [-f|--force] [--rebase|--merge] [--reference <repository>]
-             [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]
+             [--[no-]recommend-shallow] [-f|--force] [--rebase|--merge]
+             [--reference <repository>] [--depth <depth>] [--recursive]
+             [--jobs <n>] [--] [<path>...]
 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
              [commit] [--] [<path>...]
 'git submodule' [--quiet] foreach [--recursive] <command>
@@ -384,6 +385,12 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
        clone with a history truncated to the specified number of revisions.
        See linkgit:git-clone[1]
 
+--[no-]recommend-shallow::
+       This option is only valid for the update command.
+       The initial clone of a submodule will use the recommended
+       `submodule.<name>.shallow` as provided by the .gitmodules file
+       by default. To ignore the suggestions use `--no-recommend-shallow`.
+
 -j <n>::
 --jobs <n>::
        This option is only valid for the update command.
index 0abc806ea9072e563a7bdc1905442180cbc6c27a..822ad593af973dd7a1217cffaef7106876d377c2 100644 (file)
@@ -9,8 +9,8 @@ git-upload-pack - Send objects packed back to git-fetch-pack
 SYNOPSIS
 --------
 [verse]
-'git-upload-pack' [--strict] [--timeout=<n>] <directory>
-
+'git-upload-pack' [--[no-]strict] [--timeout=<n>] [--stateless-rpc]
+                 [--advertise-refs] <directory>
 DESCRIPTION
 -----------
 Invoked by 'git fetch-pack', learns what
@@ -25,12 +25,22 @@ repository.  For push operations, see 'git send-pack'.
 OPTIONS
 -------
 
---strict::
+--[no-]strict::
        Do not try <directory>/.git/ if <directory> is no Git directory.
 
 --timeout=<n>::
        Interrupt transfer after <n> seconds of inactivity.
 
+--stateless-rpc::
+       Perform only a single read-write cycle with stdin and stdout.
+       This fits with the HTTP POST request processing model where
+       a program may read the request, write a response, and must exit.
+
+--advertise-refs::
+       Only the initial ref advertisement is output, and the program exits
+       immediately. This fits with the HTTP GET request model, where
+       no request content is received but a response must be produced.
+
 <directory>::
        The repository to sync from.
 
index c62234538ba6be8c82b9a5b86c362606b80cde2a..7c4cfb0885f44619761708cb2a2d12356fbe011e 100644 (file)
@@ -10,8 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
-'git worktree prune' [-n] [-v] [--expire <expire>]
 'git worktree list' [--porcelain]
+'git worktree prune' [-n] [-v] [--expire <expire>]
 
 DESCRIPTION
 -----------
@@ -48,16 +48,13 @@ add <path> [<branch>]::
 
 Create `<path>` and checkout `<branch>` into it. The new working directory
 is linked to the current repository, sharing everything except working
-directory specific files such as HEAD, index, etc.
+directory specific files such as HEAD, index, etc. `-` may also be
+specified as `<branch>`; it is synonymous with `@{-1}`.
 +
 If `<branch>` is omitted and neither `-b` nor `-B` nor `--detached` used,
 then, as a convenience, a new branch based at HEAD is created automatically,
 as if `-b $(basename <path>)` was specified.
 
-prune::
-
-Prune working tree information in $GIT_DIR/worktrees.
-
 list::
 
 List details of each worktree.  The main worktree is listed first, followed by
@@ -65,6 +62,10 @@ each of the linked worktrees.  The output details include if the worktree is
 bare, the revision currently checked out, and the branch currently checked out
 (or 'detached HEAD' if none).
 
+prune::
+
+Prune working tree information in $GIT_DIR/worktrees.
+
 OPTIONS
 -------
 
index a474fa1a21f1a9d0ec2be3ad10a548bbb3b1a9b0..bb324358742e3aedba35874e687682b3bfd2fcfd 100644 (file)
@@ -1084,6 +1084,14 @@ of clones and fetches.
        cloning of shallow repositories.
        See `GIT_TRACE` for available trace output options.
 
+`GIT_TRACE_CURL`::
+       Enables a curl full trace dump of all incoming and outgoing data,
+       including descriptive information, of the git transport protocol.
+       This is similar to doing curl `--trace-ascii` on the command line.
+       This option overrides setting the `GIT_CURL_VERBOSE` environment
+       variable.
+       See `GIT_TRACE` for available trace output options.
+
 `GIT_LITERAL_PATHSPECS`::
        Setting this variable to `1` will cause Git to treat all
        pathspecs literally, rather than as glob patterns. For example,
index e3b1de80335e713507875fb54c31c3ba5dab8c9f..8882a3e9148622a1e9feb69679766663928b89e3 100644 (file)
@@ -525,6 +525,8 @@ patterns are available:
 
 - `csharp` suitable for source code in the C# language.
 
+- `css` suitable for cascading style sheets.
+
 - `fortran` suitable for source code in the Fortran language.
 
 - `fountain` suitable for Fountain documents.
index 2dadb9c219a6a8b0c85f35fc40877fa83aa43274..0fe02a6ce28212200cada0cdce2b19cbc3937cff 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.9.1
+DEF_VER=v2.9.0.GIT
 
 LF='
 '
index 5f1100e4013683cb25f21fe825e410f4e003029b..62615ffa4e97803da96aefbc798ab50f949a8db7 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.9.1.txt
\ No newline at end of file
+Documentation/RelNotes/2.10.0.txt
\ No newline at end of file
index 5d735ae6039f0d9708d61a27a490953ff27badf5..42df9748d668744c213e8eba0ae4b710829e1cfe 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -322,7 +322,7 @@ static int path_exists(struct tree *tree, const char *path)
        pathspec.recursive = 1;
        ret = read_tree_recursive(tree, "", 0, 0, &pathspec,
                                  reject_entry, &pathspec);
-       free_pathspec(&pathspec);
+       clear_pathspec(&pathspec);
        return ret != 0;
 }
 
index 3dfe70b7a039afadf2de4dfe8f809e3321287004..d5da5fe0900c6138337ee3f185884e454049eecd 100644 (file)
@@ -70,7 +70,8 @@ enum patch_format {
        PATCH_FORMAT_MBOX,
        PATCH_FORMAT_STGIT,
        PATCH_FORMAT_STGIT_SERIES,
-       PATCH_FORMAT_HG
+       PATCH_FORMAT_HG,
+       PATCH_FORMAT_MBOXRD
 };
 
 enum keep_type {
@@ -712,7 +713,8 @@ static int detect_patch_format(const char **paths)
  * Splits out individual email patches from `paths`, where each path is either
  * a mbox file or a Maildir. Returns 0 on success, -1 on failure.
  */
-static int split_mail_mbox(struct am_state *state, const char **paths, int keep_cr)
+static int split_mail_mbox(struct am_state *state, const char **paths,
+                               int keep_cr, int mboxrd)
 {
        struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf last = STRBUF_INIT;
@@ -724,6 +726,8 @@ static int split_mail_mbox(struct am_state *state, const char **paths, int keep_
        argv_array_push(&cp.args, "-b");
        if (keep_cr)
                argv_array_push(&cp.args, "--keep-cr");
+       if (mboxrd)
+               argv_array_push(&cp.args, "--mboxrd");
        argv_array_push(&cp.args, "--");
        argv_array_pushv(&cp.args, paths);
 
@@ -965,13 +969,15 @@ static int split_mail(struct am_state *state, enum patch_format patch_format,
 
        switch (patch_format) {
        case PATCH_FORMAT_MBOX:
-               return split_mail_mbox(state, paths, keep_cr);
+               return split_mail_mbox(state, paths, keep_cr, 0);
        case PATCH_FORMAT_STGIT:
                return split_mail_conv(stgit_patch_to_mail, state, paths, keep_cr);
        case PATCH_FORMAT_STGIT_SERIES:
                return split_mail_stgit_series(state, paths, keep_cr);
        case PATCH_FORMAT_HG:
                return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr);
+       case PATCH_FORMAT_MBOXRD:
+               return split_mail_mbox(state, paths, keep_cr, 1);
        default:
                die("BUG: invalid patch_format");
        }
@@ -2201,6 +2207,8 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int
                *opt_value = PATCH_FORMAT_STGIT_SERIES;
        else if (!strcmp(arg, "hg"))
                *opt_value = PATCH_FORMAT_HG;
+       else if (!strcmp(arg, "mboxrd"))
+               *opt_value = PATCH_FORMAT_MBOXRD;
        else
                return error(_("Invalid value for --patch-format: %s"), arg);
        return 0;
index c770d7d3d9fd923957478667291a77d33935ebe5..7fc5c3e30745d50a05accb3a83ccfa8871963df6 100644 (file)
 #include "ll-merge.h"
 #include "rerere.h"
 
-/*
- *  --check turns on checking that the working tree matches the
- *    files that are being modified, but doesn't apply the patch
- *  --stat does just a diffstat, and doesn't actually apply
- *  --numstat does numeric diffstat, and doesn't actually apply
- *  --index-info shows the old and new index info for paths if available.
- *  --index updates the cache as well.
- *  --cached updates only the cache without ever touching the working tree.
- */
-static const char *prefix;
-static int prefix_length = -1;
-static int newfd = -1;
-
-static int unidiff_zero;
-static int p_value = 1;
-static int p_value_known;
-static int check_index;
-static int update_index;
-static int cached;
-static int diffstat;
-static int numstat;
-static int summary;
-static int check;
-static int apply = 1;
-static int apply_in_reverse;
-static int apply_with_reject;
-static int apply_verbosely;
-static int allow_overlap;
-static int no_add;
-static int threeway;
-static int unsafe_paths;
-static const char *fake_ancestor;
-static int line_termination = '\n';
-static unsigned int p_context = UINT_MAX;
-static const char * const apply_usage[] = {
-       N_("git apply [<options>] [<patch>...]"),
-       NULL
-};
-
-static enum ws_error_action {
+enum ws_error_action {
        nowarn_ws_error,
        warn_on_ws_error,
        die_on_ws_error,
        correct_ws_error
-} ws_error_action = warn_on_ws_error;
-static int whitespace_error;
-static int squelch_whitespace_errors = 5;
-static int applied_after_fixing_ws;
+};
+
 
-static enum ws_ignore {
+enum ws_ignore {
        ignore_ws_none,
        ignore_ws_change
-} ws_ignore_action = ignore_ws_none;
+};
+
+/*
+ * We need to keep track of how symlinks in the preimage are
+ * manipulated by the patches.  A patch to add a/b/c where a/b
+ * is a symlink should not be allowed to affect the directory
+ * the symlink points at, but if the same patch removes a/b,
+ * it is perfectly fine, as the patch removes a/b to make room
+ * to create a directory a/b so that a/b/c can be created.
+ *
+ * See also "struct string_list symlink_changes" in "struct
+ * apply_state".
+ */
+#define SYMLINK_GOES_AWAY 01
+#define SYMLINK_IN_RESULT 02
 
+struct apply_state {
+       const char *prefix;
+       int prefix_length;
+
+       /* These are lock_file related */
+       struct lock_file *lock_file;
+       int newfd;
+
+       /* These control what gets looked at and modified */
+       int apply; /* this is not a dry-run */
+       int cached; /* apply to the index only */
+       int check; /* preimage must match working tree, don't actually apply */
+       int check_index; /* preimage must match the indexed version */
+       int update_index; /* check_index && apply */
+
+       /* These control cosmetic aspect of the output */
+       int diffstat; /* just show a diffstat, and don't actually apply */
+       int numstat; /* just show a numeric diffstat, and don't actually apply */
+       int summary; /* just report creation, deletion, etc, and don't actually apply */
+
+       /* These boolean parameters control how the apply is done */
+       int allow_overlap;
+       int apply_in_reverse;
+       int apply_with_reject;
+       int apply_verbosely;
+       int no_add;
+       int threeway;
+       int unidiff_zero;
+       int unsafe_paths;
+
+       /* Other non boolean parameters */
+       const char *fake_ancestor;
+       const char *patch_input_file;
+       int line_termination;
+       struct strbuf root;
+       int p_value;
+       int p_value_known;
+       unsigned int p_context;
+
+       /* Exclude and include path parameters */
+       struct string_list limit_by_name;
+       int has_include;
+
+       /* Various "current state" */
+       int linenr; /* current line number */
+       struct string_list symlink_changes; /* we have to track symlinks */
 
-static const char *patch_input_file;
-static struct strbuf root = STRBUF_INIT;
-static int read_stdin = 1;
-static int options;
+       /*
+        * For "diff-stat" like behaviour, we keep track of the biggest change
+        * we've seen, and the longest filename. That allows us to do simple
+        * scaling.
+        */
+       int max_change;
+       int max_len;
 
-static void parse_whitespace_option(const char *option)
+       /*
+        * Records filenames that have been touched, in order to handle
+        * the case where more than one patches touch the same file.
+        */
+       struct string_list fn_table;
+
+       /* These control whitespace errors */
+       enum ws_error_action ws_error_action;
+       enum ws_ignore ws_ignore_action;
+       const char *whitespace_option;
+       int whitespace_error;
+       int squelch_whitespace_errors;
+       int applied_after_fixing_ws;
+};
+
+static const char * const apply_usage[] = {
+       N_("git apply [<options>] [<patch>...]"),
+       NULL
+};
+
+static void parse_whitespace_option(struct apply_state *state, const char *option)
 {
        if (!option) {
-               ws_error_action = warn_on_ws_error;
+               state->ws_error_action = warn_on_ws_error;
                return;
        }
        if (!strcmp(option, "warn")) {
-               ws_error_action = warn_on_ws_error;
+               state->ws_error_action = warn_on_ws_error;
                return;
        }
        if (!strcmp(option, "nowarn")) {
-               ws_error_action = nowarn_ws_error;
+               state->ws_error_action = nowarn_ws_error;
                return;
        }
        if (!strcmp(option, "error")) {
-               ws_error_action = die_on_ws_error;
+               state->ws_error_action = die_on_ws_error;
                return;
        }
        if (!strcmp(option, "error-all")) {
-               ws_error_action = die_on_ws_error;
-               squelch_whitespace_errors = 0;
+               state->ws_error_action = die_on_ws_error;
+               state->squelch_whitespace_errors = 0;
                return;
        }
        if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
-               ws_error_action = correct_ws_error;
+               state->ws_error_action = correct_ws_error;
                return;
        }
        die(_("unrecognized whitespace option '%s'"), option);
 }
 
-static void parse_ignorewhitespace_option(const char *option)
+static void parse_ignorewhitespace_option(struct apply_state *state,
+                                         const char *option)
 {
        if (!option || !strcmp(option, "no") ||
            !strcmp(option, "false") || !strcmp(option, "never") ||
            !strcmp(option, "none")) {
-               ws_ignore_action = ignore_ws_none;
+               state->ws_ignore_action = ignore_ws_none;
                return;
        }
        if (!strcmp(option, "change")) {
-               ws_ignore_action = ignore_ws_change;
+               state->ws_ignore_action = ignore_ws_change;
                return;
        }
        die(_("unrecognized whitespace ignore option '%s'"), option);
 }
 
-static void set_default_whitespace_mode(const char *whitespace_option)
+static void set_default_whitespace_mode(struct apply_state *state)
 {
-       if (!whitespace_option && !apply_default_whitespace)
-               ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);
+       if (!state->whitespace_option && !apply_default_whitespace)
+               state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error);
 }
 
-/*
- * For "diff-stat" like behaviour, we keep track of the biggest change
- * we've seen, and the longest filename. That allows us to do simple
- * scaling.
- */
-static int max_change, max_len;
-
-/*
- * Various "current state", notably line numbers and what
- * file (and how) we're patching right now.. The "is_xxxx"
- * things are flags, where -1 means "don't know yet".
- */
-static int linenr = 1;
-
 /*
  * This represents one "hunk" from a patch, starting with
  * "@@ -oldpos,oldlines +newpos,newlines @@" marker.  The
@@ -265,13 +294,6 @@ struct image {
        struct line *line;
 };
 
-/*
- * Records filenames that have been touched, in order to handle
- * the case where more than one patches touch the same file.
- */
-
-static struct string_list fn_table;
-
 static uint32_t hash_line(const char *cp, size_t len)
 {
        size_t i;
@@ -469,7 +491,10 @@ static char *squash_slash(char *name)
        return name;
 }
 
-static char *find_name_gnu(const char *line, const char *def, int p_value)
+static char *find_name_gnu(struct apply_state *state,
+                          const char *line,
+                          const char *def,
+                          int p_value)
 {
        struct strbuf name = STRBUF_INIT;
        char *cp;
@@ -493,8 +518,8 @@ static char *find_name_gnu(const char *line, const char *def, int p_value)
        }
 
        strbuf_remove(&name, 0, cp - name.buf);
-       if (root.len)
-               strbuf_insert(&name, 0, root.buf, root.len);
+       if (state->root.len)
+               strbuf_insert(&name, 0, state->root.buf, state->root.len);
        return squash_slash(strbuf_detach(&name, NULL));
 }
 
@@ -657,8 +682,12 @@ static size_t diff_timestamp_len(const char *line, size_t len)
        return line + len - end;
 }
 
-static char *find_name_common(const char *line, const char *def,
-                             int p_value, const char *end, int terminate)
+static char *find_name_common(struct apply_state *state,
+                             const char *line,
+                             const char *def,
+                             int p_value,
+                             const char *end,
+                             int terminate)
 {
        int len;
        const char *start = NULL;
@@ -696,32 +725,39 @@ static char *find_name_common(const char *line, const char *def,
                        return squash_slash(xstrdup(def));
        }
 
-       if (root.len) {
-               char *ret = xstrfmt("%s%.*s", root.buf, len, start);
+       if (state->root.len) {
+               char *ret = xstrfmt("%s%.*s", state->root.buf, len, start);
                return squash_slash(ret);
        }
 
        return squash_slash(xmemdupz(start, len));
 }
 
-static char *find_name(const char *line, char *def, int p_value, int terminate)
+static char *find_name(struct apply_state *state,
+                      const char *line,
+                      char *def,
+                      int p_value,
+                      int terminate)
 {
        if (*line == '"') {
-               char *name = find_name_gnu(line, def, p_value);
+               char *name = find_name_gnu(state, line, def, p_value);
                if (name)
                        return name;
        }
 
-       return find_name_common(line, def, p_value, NULL, terminate);
+       return find_name_common(state, line, def, p_value, NULL, terminate);
 }
 
-static char *find_name_traditional(const char *line, char *def, int p_value)
+static char *find_name_traditional(struct apply_state *state,
+                                  const char *line,
+                                  char *def,
+                                  int p_value)
 {
        size_t len;
        size_t date_len;
 
        if (*line == '"') {
-               char *name = find_name_gnu(line, def, p_value);
+               char *name = find_name_gnu(state, line, def, p_value);
                if (name)
                        return name;
        }
@@ -729,10 +765,10 @@ static char *find_name_traditional(const char *line, char *def, int p_value)
        len = strchrnul(line, '\n') - line;
        date_len = diff_timestamp_len(line, len);
        if (!date_len)
-               return find_name_common(line, def, p_value, NULL, TERM_TAB);
+               return find_name_common(state, line, def, p_value, NULL, TERM_TAB);
        len -= date_len;
 
-       return find_name_common(line, def, p_value, line + len, 0);
+       return find_name_common(state, line, def, p_value, line + len, 0);
 }
 
 static int count_slashes(const char *cp)
@@ -750,30 +786,30 @@ static int count_slashes(const char *cp)
  * Given the string after "--- " or "+++ ", guess the appropriate
  * p_value for the given patch.
  */
-static int guess_p_value(const char *nameline)
+static int guess_p_value(struct apply_state *state, const char *nameline)
 {
        char *name, *cp;
        int val = -1;
 
        if (is_dev_null(nameline))
                return -1;
-       name = find_name_traditional(nameline, NULL, 0);
+       name = find_name_traditional(state, nameline, NULL, 0);
        if (!name)
                return -1;
        cp = strchr(name, '/');
        if (!cp)
                val = 0;
-       else if (prefix) {
+       else if (state->prefix) {
                /*
                 * Does it begin with "a/$our-prefix" and such?  Then this is
                 * very likely to apply to our directory.
                 */
-               if (!strncmp(name, prefix, prefix_length))
-                       val = count_slashes(prefix);
+               if (!strncmp(name, state->prefix, state->prefix_length))
+                       val = count_slashes(state->prefix);
                else {
                        cp++;
-                       if (!strncmp(cp, prefix, prefix_length))
-                               val = count_slashes(prefix) + 1;
+                       if (!strncmp(cp, state->prefix, state->prefix_length))
+                               val = count_slashes(state->prefix) + 1;
                }
        }
        free(name);
@@ -860,36 +896,39 @@ static int has_epoch_timestamp(const char *nameline)
  * files, we can happily check the index for a match, but for creating a
  * new file we should try to match whatever "patch" does. I have no idea.
  */
-static void parse_traditional_patch(const char *first, const char *second, struct patch *patch)
+static void parse_traditional_patch(struct apply_state *state,
+                                   const char *first,
+                                   const char *second,
+                                   struct patch *patch)
 {
        char *name;
 
        first += 4;     /* skip "--- " */
        second += 4;    /* skip "+++ " */
-       if (!p_value_known) {
+       if (!state->p_value_known) {
                int p, q;
-               p = guess_p_value(first);
-               q = guess_p_value(second);
+               p = guess_p_value(state, first);
+               q = guess_p_value(state, second);
                if (p < 0) p = q;
                if (0 <= p && p == q) {
-                       p_value = p;
-                       p_value_known = 1;
+                       state->p_value = p;
+                       state->p_value_known = 1;
                }
        }
        if (is_dev_null(first)) {
                patch->is_new = 1;
                patch->is_delete = 0;
-               name = find_name_traditional(second, NULL, p_value);
+               name = find_name_traditional(state, second, NULL, state->p_value);
                patch->new_name = name;
        } else if (is_dev_null(second)) {
                patch->is_new = 0;
                patch->is_delete = 1;
-               name = find_name_traditional(first, NULL, p_value);
+               name = find_name_traditional(state, first, NULL, state->p_value);
                patch->old_name = name;
        } else {
                char *first_name;
-               first_name = find_name_traditional(first, NULL, p_value);
-               name = find_name_traditional(second, first_name, p_value);
+               first_name = find_name_traditional(state, first, NULL, state->p_value);
+               name = find_name_traditional(state, second, first_name, state->p_value);
                free(first_name);
                if (has_epoch_timestamp(first)) {
                        patch->is_new = 1;
@@ -905,10 +944,12 @@ static void parse_traditional_patch(const char *first, const char *second, struc
                }
        }
        if (!name)
-               die(_("unable to find filename in patch at line %d"), linenr);
+               die(_("unable to find filename in patch at line %d"), state->linenr);
 }
 
-static int gitdiff_hdrend(const char *line, struct patch *patch)
+static int gitdiff_hdrend(struct apply_state *state,
+                         const char *line,
+                         struct patch *patch)
 {
        return -1;
 }
@@ -925,107 +966,135 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
 #define DIFF_OLD_NAME 0
 #define DIFF_NEW_NAME 1
 
-static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, int side)
+static void gitdiff_verify_name(struct apply_state *state,
+                               const char *line,
+                               int isnull,
+                               char **name,
+                               int side)
 {
-       if (!orig_name && !isnull)
-               return find_name(line, NULL, p_value, TERM_TAB);
+       if (!*name && !isnull) {
+               *name = find_name(state, line, NULL, state->p_value, TERM_TAB);
+               return;
+       }
 
-       if (orig_name) {
-               int len = strlen(orig_name);
+       if (*name) {
+               int len = strlen(*name);
                char *another;
                if (isnull)
                        die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"),
-                           orig_name, linenr);
-               another = find_name(line, NULL, p_value, TERM_TAB);
-               if (!another || memcmp(another, orig_name, len + 1))
+                           *name, state->linenr);
+               another = find_name(state, line, NULL, state->p_value, TERM_TAB);
+               if (!another || memcmp(another, *name, len + 1))
                        die((side == DIFF_NEW_NAME) ?
                            _("git apply: bad git-diff - inconsistent new filename on line %d") :
-                           _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr);
+                           _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr);
                free(another);
-               return orig_name;
        } else {
                /* expect "/dev/null" */
                if (memcmp("/dev/null", line, 9) || line[9] != '\n')
-                       die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr);
-               return NULL;
+                       die(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
        }
 }
 
-static int gitdiff_oldname(const char *line, struct patch *patch)
+static int gitdiff_oldname(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
-       patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name,
-                                             DIFF_OLD_NAME);
+       gitdiff_verify_name(state, line,
+                           patch->is_new, &patch->old_name,
+                           DIFF_OLD_NAME);
        return 0;
 }
 
-static int gitdiff_newname(const char *line, struct patch *patch)
+static int gitdiff_newname(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
-       patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name,
-                                             DIFF_NEW_NAME);
+       gitdiff_verify_name(state, line,
+                           patch->is_delete, &patch->new_name,
+                           DIFF_NEW_NAME);
        return 0;
 }
 
-static int gitdiff_oldmode(const char *line, struct patch *patch)
+static int gitdiff_oldmode(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
        patch->old_mode = strtoul(line, NULL, 8);
        return 0;
 }
 
-static int gitdiff_newmode(const char *line, struct patch *patch)
+static int gitdiff_newmode(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
        patch->new_mode = strtoul(line, NULL, 8);
        return 0;
 }
 
-static int gitdiff_delete(const char *line, struct patch *patch)
+static int gitdiff_delete(struct apply_state *state,
+                         const char *line,
+                         struct patch *patch)
 {
        patch->is_delete = 1;
        free(patch->old_name);
        patch->old_name = xstrdup_or_null(patch->def_name);
-       return gitdiff_oldmode(line, patch);
+       return gitdiff_oldmode(state, line, patch);
 }
 
-static int gitdiff_newfile(const char *line, struct patch *patch)
+static int gitdiff_newfile(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
        patch->is_new = 1;
        free(patch->new_name);
        patch->new_name = xstrdup_or_null(patch->def_name);
-       return gitdiff_newmode(line, patch);
+       return gitdiff_newmode(state, line, patch);
 }
 
-static int gitdiff_copysrc(const char *line, struct patch *patch)
+static int gitdiff_copysrc(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
        patch->is_copy = 1;
        free(patch->old_name);
-       patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+       patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
 }
 
-static int gitdiff_copydst(const char *line, struct patch *patch)
+static int gitdiff_copydst(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
        patch->is_copy = 1;
        free(patch->new_name);
-       patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+       patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
 }
 
-static int gitdiff_renamesrc(const char *line, struct patch *patch)
+static int gitdiff_renamesrc(struct apply_state *state,
+                            const char *line,
+                            struct patch *patch)
 {
        patch->is_rename = 1;
        free(patch->old_name);
-       patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+       patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
 }
 
-static int gitdiff_renamedst(const char *line, struct patch *patch)
+static int gitdiff_renamedst(struct apply_state *state,
+                            const char *line,
+                            struct patch *patch)
 {
        patch->is_rename = 1;
        free(patch->new_name);
-       patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+       patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
 }
 
-static int gitdiff_similarity(const char *line, struct patch *patch)
+static int gitdiff_similarity(struct apply_state *state,
+                             const char *line,
+                             struct patch *patch)
 {
        unsigned long val = strtoul(line, NULL, 10);
        if (val <= 100)
@@ -1033,7 +1102,9 @@ static int gitdiff_similarity(const char *line, struct patch *patch)
        return 0;
 }
 
-static int gitdiff_dissimilarity(const char *line, struct patch *patch)
+static int gitdiff_dissimilarity(struct apply_state *state,
+                                const char *line,
+                                struct patch *patch)
 {
        unsigned long val = strtoul(line, NULL, 10);
        if (val <= 100)
@@ -1041,7 +1112,9 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch)
        return 0;
 }
 
-static int gitdiff_index(const char *line, struct patch *patch)
+static int gitdiff_index(struct apply_state *state,
+                        const char *line,
+                        struct patch *patch)
 {
        /*
         * index line is N hexadecimal, "..", N hexadecimal,
@@ -1078,7 +1151,9 @@ static int gitdiff_index(const char *line, struct patch *patch)
  * This is normal for a diff that doesn't change anything: we'll fall through
  * into the next diff. Tell the parser to break out.
  */
-static int gitdiff_unrecognized(const char *line, struct patch *patch)
+static int gitdiff_unrecognized(struct apply_state *state,
+                               const char *line,
+                               struct patch *patch)
 {
        return -1;
 }
@@ -1087,15 +1162,17 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch)
  * Skip p_value leading components from "line"; as we do not accept
  * absolute paths, return NULL in that case.
  */
-static const char *skip_tree_prefix(const char *line, int llen)
+static const char *skip_tree_prefix(struct apply_state *state,
+                                   const char *line,
+                                   int llen)
 {
        int nslash;
        int i;
 
-       if (!p_value)
+       if (!state->p_value)
                return (llen && line[0] == '/') ? NULL : line;
 
-       nslash = p_value;
+       nslash = state->p_value;
        for (i = 0; i < llen; i++) {
                int ch = line[i];
                if (ch == '/' && --nslash <= 0)
@@ -1112,7 +1189,9 @@ static const char *skip_tree_prefix(const char *line, int llen)
  * creation or deletion of an empty file.  In any of these cases,
  * both sides are the same name under a/ and b/ respectively.
  */
-static char *git_header_name(const char *line, int llen)
+static char *git_header_name(struct apply_state *state,
+                            const char *line,
+                            int llen)
 {
        const char *name;
        const char *second = NULL;
@@ -1130,7 +1209,7 @@ static char *git_header_name(const char *line, int llen)
                        goto free_and_fail1;
 
                /* strip the a/b prefix including trailing slash */
-               cp = skip_tree_prefix(first.buf, first.len);
+               cp = skip_tree_prefix(state, first.buf, first.len);
                if (!cp)
                        goto free_and_fail1;
                strbuf_remove(&first, 0, cp - first.buf);
@@ -1147,7 +1226,7 @@ static char *git_header_name(const char *line, int llen)
                if (*second == '"') {
                        if (unquote_c_style(&sp, second, NULL))
                                goto free_and_fail1;
-                       cp = skip_tree_prefix(sp.buf, sp.len);
+                       cp = skip_tree_prefix(state, sp.buf, sp.len);
                        if (!cp)
                                goto free_and_fail1;
                        /* They must match, otherwise ignore */
@@ -1158,7 +1237,7 @@ static char *git_header_name(const char *line, int llen)
                }
 
                /* unquoted second */
-               cp = skip_tree_prefix(second, line + llen - second);
+               cp = skip_tree_prefix(state, second, line + llen - second);
                if (!cp)
                        goto free_and_fail1;
                if (line + llen - cp != first.len ||
@@ -1173,7 +1252,7 @@ static char *git_header_name(const char *line, int llen)
        }
 
        /* unquoted first name */
-       name = skip_tree_prefix(line, llen);
+       name = skip_tree_prefix(state, line, llen);
        if (!name)
                return NULL;
 
@@ -1189,7 +1268,7 @@ static char *git_header_name(const char *line, int llen)
                        if (unquote_c_style(&sp, second, NULL))
                                goto free_and_fail2;
 
-                       np = skip_tree_prefix(sp.buf, sp.len);
+                       np = skip_tree_prefix(state, sp.buf, sp.len);
                        if (!np)
                                goto free_and_fail2;
 
@@ -1233,7 +1312,7 @@ static char *git_header_name(const char *line, int llen)
                         */
                        if (!name[len + 1])
                                return NULL; /* no postimage name */
-                       second = skip_tree_prefix(name + len + 1,
+                       second = skip_tree_prefix(state, name + len + 1,
                                                  line_len - (len + 1));
                        if (!second)
                                return NULL;
@@ -1249,7 +1328,11 @@ static char *git_header_name(const char *line, int llen)
 }
 
 /* Verify that we recognize the lines following a git header */
-static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch)
+static int parse_git_header(struct apply_state *state,
+                           const char *line,
+                           int len,
+                           unsigned int size,
+                           struct patch *patch)
 {
        unsigned long offset;
 
@@ -1263,20 +1346,20 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
         * or removing or adding empty files), so we get
         * the default name from the header.
         */
-       patch->def_name = git_header_name(line, len);
-       if (patch->def_name && root.len) {
-               char *s = xstrfmt("%s%s", root.buf, patch->def_name);
+       patch->def_name = git_header_name(state, line, len);
+       if (patch->def_name && state->root.len) {
+               char *s = xstrfmt("%s%s", state->root.buf, patch->def_name);
                free(patch->def_name);
                patch->def_name = s;
        }
 
        line += len;
        size -= len;
-       linenr++;
-       for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
+       state->linenr++;
+       for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {
                static const struct opentry {
                        const char *str;
-                       int (*fn)(const char *, struct patch *);
+                       int (*fn)(struct apply_state *, const char *, struct patch *);
                } optable[] = {
                        { "@@ -", gitdiff_hdrend },
                        { "--- ", gitdiff_oldname },
@@ -1306,7 +1389,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
                        int oplen = strlen(p->str);
                        if (len < oplen || memcmp(p->str, line, oplen))
                                continue;
-                       if (p->fn(line + oplen, patch) < 0)
+                       if (p->fn(state, line + oplen, patch) < 0)
                                return offset;
                        break;
                }
@@ -1431,7 +1514,11 @@ static int parse_fragment_header(const char *line, int len, struct fragment *fra
        return offset;
 }
 
-static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch)
+static int find_header(struct apply_state *state,
+                      const char *line,
+                      unsigned long size,
+                      int *hdrsize,
+                      struct patch *patch)
 {
        unsigned long offset, len;
 
@@ -1440,7 +1527,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
        patch->is_new = patch->is_delete = -1;
        patch->old_mode = patch->new_mode = 0;
        patch->old_name = patch->new_name = NULL;
-       for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
+       for (offset = 0; size > 0; offset += len, size -= len, line += len, state->linenr++) {
                unsigned long nextlen;
 
                len = linelen(line, size);
@@ -1461,7 +1548,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
                        if (parse_fragment_header(line, len, &dummy) < 0)
                                continue;
                        die(_("patch fragment without header at line %d: %.*s"),
-                           linenr, (int)len-1, line);
+                           state->linenr, (int)len-1, line);
                }
 
                if (size < len + 6)
@@ -1472,7 +1559,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
                 * or mode change, so we handle that specially
                 */
                if (!memcmp("diff --git ", line, 11)) {
-                       int git_hdr_len = parse_git_header(line, len, size, patch);
+                       int git_hdr_len = parse_git_header(state, line, len, size, patch);
                        if (git_hdr_len <= len)
                                continue;
                        if (!patch->old_name && !patch->new_name) {
@@ -1481,14 +1568,14 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
                                               "%d leading pathname component (line %d)",
                                               "git diff header lacks filename information when removing "
                                               "%d leading pathname components (line %d)",
-                                              p_value),
-                                           p_value, linenr);
+                                              state->p_value),
+                                           state->p_value, state->linenr);
                                patch->old_name = xstrdup(patch->def_name);
                                patch->new_name = xstrdup(patch->def_name);
                        }
                        if (!patch->is_delete && !patch->new_name)
                                die("git diff header lacks filename information "
-                                   "(line %d)", linenr);
+                                   "(line %d)", state->linenr);
                        patch->is_toplevel_relative = 1;
                        *hdrsize = git_hdr_len;
                        return offset;
@@ -1508,37 +1595,44 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
                        continue;
 
                /* Ok, we'll consider it a patch */
-               parse_traditional_patch(line, line+len, patch);
+               parse_traditional_patch(state, line, line+len, patch);
                *hdrsize = len + nextlen;
-               linenr += 2;
+               state->linenr += 2;
                return offset;
        }
        return -1;
 }
 
-static void record_ws_error(unsigned result, const char *line, int len, int linenr)
+static void record_ws_error(struct apply_state *state,
+                           unsigned result,
+                           const char *line,
+                           int len,
+                           int linenr)
 {
        char *err;
 
        if (!result)
                return;
 
-       whitespace_error++;
-       if (squelch_whitespace_errors &&
-           squelch_whitespace_errors < whitespace_error)
+       state->whitespace_error++;
+       if (state->squelch_whitespace_errors &&
+           state->squelch_whitespace_errors < state->whitespace_error)
                return;
 
        err = whitespace_error_string(result);
        fprintf(stderr, "%s:%d: %s.\n%.*s\n",
-               patch_input_file, linenr, err, len, line);
+               state->patch_input_file, linenr, err, len, line);
        free(err);
 }
 
-static void check_whitespace(const char *line, int len, unsigned ws_rule)
+static void check_whitespace(struct apply_state *state,
+                            const char *line,
+                            int len,
+                            unsigned ws_rule)
 {
        unsigned result = ws_check(line + 1, len - 1, ws_rule);
 
-       record_ws_error(result, line + 1, len - 2, linenr);
+       record_ws_error(state, result, line + 1, len - 2, state->linenr);
 }
 
 /*
@@ -1547,8 +1641,11 @@ static void check_whitespace(const char *line, int len, unsigned ws_rule)
  * between a "---" that is part of a patch, and a "---" that starts
  * the next patch is to look at the line counts..
  */
-static int parse_fragment(const char *line, unsigned long size,
-                         struct patch *patch, struct fragment *fragment)
+static int parse_fragment(struct apply_state *state,
+                         const char *line,
+                         unsigned long size,
+                         struct patch *patch,
+                         struct fragment *fragment)
 {
        int added, deleted;
        int len = linelen(line, size), offset;
@@ -1568,11 +1665,11 @@ static int parse_fragment(const char *line, unsigned long size,
        /* Parse the thing.. */
        line += len;
        size -= len;
-       linenr++;
+       state->linenr++;
        added = deleted = 0;
        for (offset = len;
             0 < size;
-            offset += len, size -= len, line += len, linenr++) {
+            offset += len, size -= len, line += len, state->linenr++) {
                if (!oldlines && !newlines)
                        break;
                len = linelen(line, size);
@@ -1588,22 +1685,22 @@ static int parse_fragment(const char *line, unsigned long size,
                        if (!deleted && !added)
                                leading++;
                        trailing++;
-                       if (!apply_in_reverse &&
-                           ws_error_action == correct_ws_error)
-                               check_whitespace(line, len, patch->ws_rule);
+                       if (!state->apply_in_reverse &&
+                           state->ws_error_action == correct_ws_error)
+                               check_whitespace(state, line, len, patch->ws_rule);
                        break;
                case '-':
-                       if (apply_in_reverse &&
-                           ws_error_action != nowarn_ws_error)
-                               check_whitespace(line, len, patch->ws_rule);
+                       if (state->apply_in_reverse &&
+                           state->ws_error_action != nowarn_ws_error)
+                               check_whitespace(state, line, len, patch->ws_rule);
                        deleted++;
                        oldlines--;
                        trailing = 0;
                        break;
                case '+':
-                       if (!apply_in_reverse &&
-                           ws_error_action != nowarn_ws_error)
-                               check_whitespace(line, len, patch->ws_rule);
+                       if (!state->apply_in_reverse &&
+                           state->ws_error_action != nowarn_ws_error)
+                               check_whitespace(state, line, len, patch->ws_rule);
                        added++;
                        newlines--;
                        trailing = 0;
@@ -1657,7 +1754,10 @@ static int parse_fragment(const char *line, unsigned long size,
  * The (fragment->patch, fragment->size) pair points into the memory given
  * by the caller, not a copy, when we return.
  */
-static int parse_single_patch(const char *line, unsigned long size, struct patch *patch)
+static int parse_single_patch(struct apply_state *state,
+                             const char *line,
+                             unsigned long size,
+                             struct patch *patch)
 {
        unsigned long offset = 0;
        unsigned long oldlines = 0, newlines = 0, context = 0;
@@ -1668,10 +1768,10 @@ static int parse_single_patch(const char *line, unsigned long size, struct patch
                int len;
 
                fragment = xcalloc(1, sizeof(*fragment));
-               fragment->linenr = linenr;
-               len = parse_fragment(line, size, patch, fragment);
+               fragment->linenr = state->linenr;
+               len = parse_fragment(state, line, size, patch, fragment);
                if (len <= 0)
-                       die(_("corrupt patch at line %d"), linenr);
+                       die(_("corrupt patch at line %d"), state->linenr);
                fragment->patch = line;
                fragment->size = len;
                oldlines += fragment->oldlines;
@@ -1757,7 +1857,8 @@ static char *inflate_it(const void *data, unsigned long size,
  * points at an allocated memory that the caller must free, so
  * it is marked as "->free_patch = 1".
  */
-static struct fragment *parse_binary_hunk(char **buf_p,
+static struct fragment *parse_binary_hunk(struct apply_state *state,
+                                         char **buf_p,
                                          unsigned long *sz_p,
                                          int *status_p,
                                          int *used_p)
@@ -1799,13 +1900,13 @@ static struct fragment *parse_binary_hunk(char **buf_p,
        else
                return NULL;
 
-       linenr++;
+       state->linenr++;
        buffer += llen;
        while (1) {
                int byte_length, max_byte_length, newsize;
                llen = linelen(buffer, size);
                used += llen;
-               linenr++;
+               state->linenr++;
                if (llen == 1) {
                        /* consume the blank line */
                        buffer++;
@@ -1859,7 +1960,7 @@ static struct fragment *parse_binary_hunk(char **buf_p,
        free(data);
        *status_p = -1;
        error(_("corrupt binary patch at line %d: %.*s"),
-             linenr-1, llen-1, buffer);
+             state->linenr-1, llen-1, buffer);
        return NULL;
 }
 
@@ -1868,7 +1969,10 @@ static struct fragment *parse_binary_hunk(char **buf_p,
  *   -1 in case of error,
  *   the length of the parsed binary patch otherwise
  */
-static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+static int parse_binary(struct apply_state *state,
+                       char *buffer,
+                       unsigned long size,
+                       struct patch *patch)
 {
        /*
         * We have read "GIT binary patch\n"; what follows is a line
@@ -1889,15 +1993,15 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
        int status;
        int used, used_1;
 
-       forward = parse_binary_hunk(&buffer, &size, &status, &used);
+       forward = parse_binary_hunk(state, &buffer, &size, &status, &used);
        if (!forward && !status)
                /* there has to be one hunk (forward hunk) */
-               return error(_("unrecognized binary patch at line %d"), linenr-1);
+               return error(_("unrecognized binary patch at line %d"), state->linenr-1);
        if (status)
                /* otherwise we already gave an error message */
                return status;
 
-       reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
+       reverse = parse_binary_hunk(state, &buffer, &size, &status, &used_1);
        if (reverse)
                used += used_1;
        else if (status) {
@@ -1915,53 +2019,53 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
        return used;
 }
 
-static void prefix_one(char **name)
+static void prefix_one(struct apply_state *state, char **name)
 {
        char *old_name = *name;
        if (!old_name)
                return;
-       *name = xstrdup(prefix_filename(prefix, prefix_length, *name));
+       *name = xstrdup(prefix_filename(state->prefix, state->prefix_length, *name));
        free(old_name);
 }
 
-static void prefix_patch(struct patch *p)
+static void prefix_patch(struct apply_state *state, struct patch *p)
 {
-       if (!prefix || p->is_toplevel_relative)
+       if (!state->prefix || p->is_toplevel_relative)
                return;
-       prefix_one(&p->new_name);
-       prefix_one(&p->old_name);
+       prefix_one(state, &p->new_name);
+       prefix_one(state, &p->old_name);
 }
 
 /*
  * include/exclude
  */
 
-static struct string_list limit_by_name;
-static int has_include;
-static void add_name_limit(const char *name, int exclude)
+static void add_name_limit(struct apply_state *state,
+                          const char *name,
+                          int exclude)
 {
        struct string_list_item *it;
 
-       it = string_list_append(&limit_by_name, name);
+       it = string_list_append(&state->limit_by_name, name);
        it->util = exclude ? NULL : (void *) 1;
 }
 
-static int use_patch(struct patch *p)
+static int use_patch(struct apply_state *state, struct patch *p)
 {
        const char *pathname = p->new_name ? p->new_name : p->old_name;
        int i;
 
        /* Paths outside are not touched regardless of "--include" */
-       if (0 < prefix_length) {
+       if (0 < state->prefix_length) {
                int pathlen = strlen(pathname);
-               if (pathlen <= prefix_length ||
-                   memcmp(prefix, pathname, prefix_length))
+               if (pathlen <= state->prefix_length ||
+                   memcmp(state->prefix, pathname, state->prefix_length))
                        return 0;
        }
 
        /* See if it matches any of exclude/include rule */
-       for (i = 0; i < limit_by_name.nr; i++) {
-               struct string_list_item *it = &limit_by_name.items[i];
+       for (i = 0; i < state->limit_by_name.nr; i++) {
+               struct string_list_item *it = &state->limit_by_name.items[i];
                if (!wildmatch(it->string, pathname, 0, NULL))
                        return (it->util != NULL);
        }
@@ -1971,7 +2075,7 @@ static int use_patch(struct patch *p)
         * not used.  Otherwise, we saw bunch of exclude rules (or none)
         * and such a path is used.
         */
-       return !has_include;
+       return !state->has_include;
 }
 
 
@@ -1982,25 +2086,27 @@ static int use_patch(struct patch *p)
  * Return the number of bytes consumed, so that the caller can call us
  * again for the next patch.
  */
-static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
+static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch *patch)
 {
        int hdrsize, patchsize;
-       int offset = find_header(buffer, size, &hdrsize, patch);
+       int offset = find_header(state, buffer, size, &hdrsize, patch);
 
        if (offset < 0)
                return offset;
 
-       prefix_patch(patch);
+       prefix_patch(state, patch);
 
-       if (!use_patch(patch))
+       if (!use_patch(state, patch))
                patch->ws_rule = 0;
        else
                patch->ws_rule = whitespace_rule(patch->new_name
                                                 ? patch->new_name
                                                 : patch->old_name);
 
-       patchsize = parse_single_patch(buffer + offset + hdrsize,
-                                      size - offset - hdrsize, patch);
+       patchsize = parse_single_patch(state,
+                                      buffer + offset + hdrsize,
+                                      size - offset - hdrsize,
+                                      patch);
 
        if (!patchsize) {
                static const char git_binary[] = "GIT binary patch\n";
@@ -2010,8 +2116,8 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                if (llen == sizeof(git_binary) - 1 &&
                    !memcmp(git_binary, buffer + hd, llen)) {
                        int used;
-                       linenr++;
-                       used = parse_binary(buffer + hd + llen,
+                       state->linenr++;
+                       used = parse_binary(state, buffer + hd + llen,
                                            size - hd - llen, patch);
                        if (used < 0)
                                return -1;
@@ -2031,7 +2137,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                                int len = strlen(binhdr[i]);
                                if (len < size - hd &&
                                    !memcmp(binhdr[i], buffer + hd, len)) {
-                                       linenr++;
+                                       state->linenr++;
                                        patch->is_binary = 1;
                                        patchsize = llen;
                                        break;
@@ -2043,9 +2149,9 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                 * without metadata change.  A binary patch appears
                 * empty to us here.
                 */
-               if ((apply || check) &&
+               if ((state->apply || state->check) &&
                    (!patch->is_binary && !metadata_changes(patch)))
-                       die(_("patch with only garbage at line %d"), linenr);
+                       die(_("patch with only garbage at line %d"), state->linenr);
        }
 
        return offset + hdrsize + patchsize;
@@ -2083,7 +2189,7 @@ static const char pluses[] =
 static const char minuses[]=
 "----------------------------------------------------------------------";
 
-static void show_stats(struct patch *patch)
+static void show_stats(struct apply_state *state, struct patch *patch)
 {
        struct strbuf qname = STRBUF_INIT;
        char *cp = patch->new_name ? patch->new_name : patch->old_name;
@@ -2094,7 +2200,7 @@ static void show_stats(struct patch *patch)
        /*
         * "scale" the filename
         */
-       max = max_len;
+       max = state->max_len;
        if (max > 50)
                max = 50;
 
@@ -2117,13 +2223,13 @@ static void show_stats(struct patch *patch)
        /*
         * scale the add/delete
         */
-       max = max + max_change > 70 ? 70 - max : max_change;
+       max = max + state->max_change > 70 ? 70 - max : state->max_change;
        add = patch->lines_added;
        del = patch->lines_deleted;
 
-       if (max_change > 0) {
-               int total = ((add + del) * max + max_change / 2) / max_change;
-               add = (add * max + max_change / 2) / max_change;
+       if (state->max_change > 0) {
+               int total = ((add + del) * max + state->max_change / 2) / state->max_change;
+               add = (add * max + state->max_change / 2) / state->max_change;
                del = total - add;
        }
        printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
@@ -2194,17 +2300,17 @@ static void update_pre_post_images(struct image *preimage,
        fixed = preimage->buf;
 
        for (i = reduced = ctx = 0; i < postimage->nr; i++) {
-               size_t len = postimage->line[i].len;
+               size_t l_len = postimage->line[i].len;
                if (!(postimage->line[i].flag & LINE_COMMON)) {
                        /* an added line -- no counterparts in preimage */
-                       memmove(new, old, len);
-                       old += len;
-                       new += len;
+                       memmove(new, old, l_len);
+                       old += l_len;
+                       new += l_len;
                        continue;
                }
 
                /* a common context -- skip it in the original postimage */
-               old += len;
+               old += l_len;
 
                /* and find the corresponding one in the fixed preimage */
                while (ctx < preimage->nr &&
@@ -2223,11 +2329,11 @@ static void update_pre_post_images(struct image *preimage,
                }
 
                /* and copy it in, while fixing the line length */
-               len = preimage->line[ctx].len;
-               memcpy(new, fixed, len);
-               new += len;
-               fixed += len;
-               postimage->line[i].len = len;
+               l_len = preimage->line[ctx].len;
+               memcpy(new, fixed, l_len);
+               new += l_len;
+               fixed += l_len;
+               postimage->line[i].len = l_len;
                ctx++;
        }
 
@@ -2242,7 +2348,76 @@ static void update_pre_post_images(struct image *preimage,
        postimage->nr -= reduced;
 }
 
-static int match_fragment(struct image *img,
+static int line_by_line_fuzzy_match(struct image *img,
+                                   struct image *preimage,
+                                   struct image *postimage,
+                                   unsigned long try,
+                                   int try_lno,
+                                   int preimage_limit)
+{
+       int i;
+       size_t imgoff = 0;
+       size_t preoff = 0;
+       size_t postlen = postimage->len;
+       size_t extra_chars;
+       char *buf;
+       char *preimage_eof;
+       char *preimage_end;
+       struct strbuf fixed;
+       char *fixed_buf;
+       size_t fixed_len;
+
+       for (i = 0; i < preimage_limit; i++) {
+               size_t prelen = preimage->line[i].len;
+               size_t imglen = img->line[try_lno+i].len;
+
+               if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
+                                     preimage->buf + preoff, prelen))
+                       return 0;
+               if (preimage->line[i].flag & LINE_COMMON)
+                       postlen += imglen - prelen;
+               imgoff += imglen;
+               preoff += prelen;
+       }
+
+       /*
+        * Ok, the preimage matches with whitespace fuzz.
+        *
+        * imgoff now holds the true length of the target that
+        * matches the preimage before the end of the file.
+        *
+        * Count the number of characters in the preimage that fall
+        * beyond the end of the file and make sure that all of them
+        * are whitespace characters. (This can only happen if
+        * we are removing blank lines at the end of the file.)
+        */
+       buf = preimage_eof = preimage->buf + preoff;
+       for ( ; i < preimage->nr; i++)
+               preoff += preimage->line[i].len;
+       preimage_end = preimage->buf + preoff;
+       for ( ; buf < preimage_end; buf++)
+               if (!isspace(*buf))
+                       return 0;
+
+       /*
+        * Update the preimage and the common postimage context
+        * lines to use the same whitespace as the target.
+        * If whitespace is missing in the target (i.e.
+        * if the preimage extends beyond the end of the file),
+        * use the whitespace from the preimage.
+        */
+       extra_chars = preimage_end - preimage_eof;
+       strbuf_init(&fixed, imgoff + extra_chars);
+       strbuf_add(&fixed, img->buf + try, imgoff);
+       strbuf_add(&fixed, preimage_eof, extra_chars);
+       fixed_buf = strbuf_detach(&fixed, &fixed_len);
+       update_pre_post_images(preimage, postimage,
+                              fixed_buf, fixed_len, postlen);
+       return 1;
+}
+
+static int match_fragment(struct apply_state *state,
+                         struct image *img,
                          struct image *preimage,
                          struct image *postimage,
                          unsigned long try,
@@ -2263,7 +2438,7 @@ static int match_fragment(struct image *img,
                preimage_limit = preimage->nr;
                if (match_end && (preimage->nr + try_lno != img->nr))
                        return 0;
-       } else if (ws_error_action == correct_ws_error &&
+       } else if (state->ws_error_action == correct_ws_error &&
                   (ws_rule & WS_BLANK_AT_EOF)) {
                /*
                 * This hunk extends beyond the end of img, and we are
@@ -2331,63 +2506,11 @@ static int match_fragment(struct image *img,
         * fuzzy matching. We collect all the line length information because
         * we need it to adjust whitespace if we match.
         */
-       if (ws_ignore_action == ignore_ws_change) {
-               size_t imgoff = 0;
-               size_t preoff = 0;
-               size_t postlen = postimage->len;
-               size_t extra_chars;
-               char *preimage_eof;
-               char *preimage_end;
-               for (i = 0; i < preimage_limit; i++) {
-                       size_t prelen = preimage->line[i].len;
-                       size_t imglen = img->line[try_lno+i].len;
-
-                       if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
-                                             preimage->buf + preoff, prelen))
-                               return 0;
-                       if (preimage->line[i].flag & LINE_COMMON)
-                               postlen += imglen - prelen;
-                       imgoff += imglen;
-                       preoff += prelen;
-               }
+       if (state->ws_ignore_action == ignore_ws_change)
+               return line_by_line_fuzzy_match(img, preimage, postimage,
+                                               try, try_lno, preimage_limit);
 
-               /*
-                * Ok, the preimage matches with whitespace fuzz.
-                *
-                * imgoff now holds the true length of the target that
-                * matches the preimage before the end of the file.
-                *
-                * Count the number of characters in the preimage that fall
-                * beyond the end of the file and make sure that all of them
-                * are whitespace characters. (This can only happen if
-                * we are removing blank lines at the end of the file.)
-                */
-               buf = preimage_eof = preimage->buf + preoff;
-               for ( ; i < preimage->nr; i++)
-                       preoff += preimage->line[i].len;
-               preimage_end = preimage->buf + preoff;
-               for ( ; buf < preimage_end; buf++)
-                       if (!isspace(*buf))
-                               return 0;
-
-               /*
-                * Update the preimage and the common postimage context
-                * lines to use the same whitespace as the target.
-                * If whitespace is missing in the target (i.e.
-                * if the preimage extends beyond the end of the file),
-                * use the whitespace from the preimage.
-                */
-               extra_chars = preimage_end - preimage_eof;
-               strbuf_init(&fixed, imgoff + extra_chars);
-               strbuf_add(&fixed, img->buf + try, imgoff);
-               strbuf_add(&fixed, preimage_eof, extra_chars);
-               fixed_buf = strbuf_detach(&fixed, &fixed_len);
-               update_pre_post_images(preimage, postimage,
-                               fixed_buf, fixed_len, postlen);
-               return 1;
-       }
-
-       if (ws_error_action != correct_ws_error)
+       if (state->ws_error_action != correct_ws_error)
                return 0;
 
        /*
@@ -2499,7 +2622,8 @@ static int match_fragment(struct image *img,
        return 0;
 }
 
-static int find_pos(struct image *img,
+static int find_pos(struct apply_state *state,
+                   struct image *img,
                    struct image *preimage,
                    struct image *postimage,
                    int line,
@@ -2543,7 +2667,7 @@ static int find_pos(struct image *img,
        try_lno = line;
 
        for (i = 0; ; i++) {
-               if (match_fragment(img, preimage, postimage,
+               if (match_fragment(state, img, preimage, postimage,
                                   try, try_lno, ws_rule,
                                   match_beginning, match_end))
                        return try_lno;
@@ -2594,7 +2718,8 @@ static void remove_last_line(struct image *img)
  * apply at applied_pos (counts in line numbers) in "img".
  * Update "img" to remove "preimage" and replace it with "postimage".
  */
-static void update_image(struct image *img,
+static void update_image(struct apply_state *state,
+                        struct image *img,
                         int applied_pos,
                         struct image *preimage,
                         struct image *postimage)
@@ -2659,7 +2784,7 @@ static void update_image(struct image *img,
        memcpy(img->line + applied_pos,
               postimage->line,
               postimage->nr * sizeof(*img->line));
-       if (!allow_overlap)
+       if (!state->allow_overlap)
                for (i = 0; i < postimage->nr; i++)
                        img->line[applied_pos + i].flag |= LINE_PATCHED;
        img->nr = nr;
@@ -2670,7 +2795,8 @@ static void update_image(struct image *img,
  * postimage) for the hunk.  Find lines that match "preimage" in "img" and
  * replace the part of "img" with "postimage" text.
  */
-static int apply_one_fragment(struct image *img, struct fragment *frag,
+static int apply_one_fragment(struct apply_state *state,
+                             struct image *img, struct fragment *frag,
                              int inaccurate_eof, unsigned ws_rule,
                              int nth_fragment)
 {
@@ -2715,7 +2841,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                if (len < size && patch[len] == '\\')
                        plen--;
                first = *patch;
-               if (apply_in_reverse) {
+               if (state->apply_in_reverse) {
                        if (first == '-')
                                first = '+';
                        else if (first == '+')
@@ -2748,17 +2874,17 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                /* Fall-through for ' ' */
                case '+':
                        /* --no-add does not add new lines */
-                       if (first == '+' && no_add)
+                       if (first == '+' && state->no_add)
                                break;
 
                        start = newlines.len;
                        if (first != '+' ||
-                           !whitespace_error ||
-                           ws_error_action != correct_ws_error) {
+                           !state->whitespace_error ||
+                           state->ws_error_action != correct_ws_error) {
                                strbuf_add(&newlines, patch + 1, plen);
                        }
                        else {
-                               ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+                               ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &state->applied_after_fixing_ws);
                        }
                        add_line_info(&postimage, newlines.buf + start, newlines.len - start,
                                      (first == '+' ? 0 : LINE_COMMON));
@@ -2771,7 +2897,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                        /* Ignore it, we already handled it */
                        break;
                default:
-                       if (apply_verbosely)
+                       if (state->apply_verbosely)
                                error(_("invalid start of line: '%c'"), first);
                        applied_pos = -1;
                        goto out;
@@ -2812,7 +2938,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
         * without leading context must match at the beginning.
         */
        match_beginning = (!frag->oldpos ||
-                          (frag->oldpos == 1 && !unidiff_zero));
+                          (frag->oldpos == 1 && !state->unidiff_zero));
 
        /*
         * A hunk without trailing lines must match at the end.
@@ -2820,7 +2946,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
         * from the lack of trailing lines if the patch was generated
         * with unidiff without any context.
         */
-       match_end = !unidiff_zero && !trailing;
+       match_end = !state->unidiff_zero && !trailing;
 
        pos = frag->newpos ? (frag->newpos - 1) : 0;
        preimage.buf = oldlines;
@@ -2832,14 +2958,14 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
 
        for (;;) {
 
-               applied_pos = find_pos(img, &preimage, &postimage, pos,
+               applied_pos = find_pos(state, img, &preimage, &postimage, pos,
                                       ws_rule, match_beginning, match_end);
 
                if (applied_pos >= 0)
                        break;
 
                /* Am I at my context limits? */
-               if ((leading <= p_context) && (trailing <= p_context))
+               if ((leading <= state->p_context) && (trailing <= state->p_context))
                        break;
                if (match_beginning || match_end) {
                        match_beginning = match_end = 0;
@@ -2868,10 +2994,10 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                if (new_blank_lines_at_end &&
                    preimage.nr + applied_pos >= img->nr &&
                    (ws_rule & WS_BLANK_AT_EOF) &&
-                   ws_error_action != nowarn_ws_error) {
-                       record_ws_error(WS_BLANK_AT_EOF, "+", 1,
+                   state->ws_error_action != nowarn_ws_error) {
+                       record_ws_error(state, WS_BLANK_AT_EOF, "+", 1,
                                        found_new_blank_lines_at_end);
-                       if (ws_error_action == correct_ws_error) {
+                       if (state->ws_error_action == correct_ws_error) {
                                while (new_blank_lines_at_end--)
                                        remove_last_line(&postimage);
                        }
@@ -2882,13 +3008,13 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                         * apply_patch->check_patch_list->check_patch->
                         * apply_data->apply_fragments->apply_one_fragment
                         */
-                       if (ws_error_action == die_on_ws_error)
-                               apply = 0;
+                       if (state->ws_error_action == die_on_ws_error)
+                               state->apply = 0;
                }
 
-               if (apply_verbosely && applied_pos != pos) {
+               if (state->apply_verbosely && applied_pos != pos) {
                        int offset = applied_pos - pos;
-                       if (apply_in_reverse)
+                       if (state->apply_in_reverse)
                                offset = 0 - offset;
                        fprintf_ln(stderr,
                                   Q_("Hunk #%d succeeded at %d (offset %d line).",
@@ -2906,9 +3032,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                        fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
                                             " to apply fragment at %d"),
                                   leading, trailing, applied_pos+1);
-               update_image(img, applied_pos, &preimage, &postimage);
+               update_image(state, img, applied_pos, &preimage, &postimage);
        } else {
-               if (apply_verbosely)
+               if (state->apply_verbosely)
                        error(_("while searching for:\n%.*s"),
                              (int)(old - oldlines), oldlines);
        }
@@ -2922,7 +3048,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        return (applied_pos < 0);
 }
 
-static int apply_binary_fragment(struct image *img, struct patch *patch)
+static int apply_binary_fragment(struct apply_state *state,
+                                struct image *img,
+                                struct patch *patch)
 {
        struct fragment *fragment = patch->fragments;
        unsigned long len;
@@ -2935,7 +3063,7 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
                             patch->old_name);
 
        /* Binary patch is irreversible without the optional second hunk */
-       if (apply_in_reverse) {
+       if (state->apply_in_reverse) {
                if (!fragment->next)
                        return error("cannot reverse-apply a binary patch "
                                     "without the reverse hunk to '%s'",
@@ -2968,7 +3096,9 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
  * but the preimage prepared by the caller in "img" is freed here
  * or in the helper function apply_binary_fragment() this calls.
  */
-static int apply_binary(struct image *img, struct patch *patch)
+static int apply_binary(struct apply_state *state,
+                       struct image *img,
+                       struct patch *patch)
 {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
@@ -3029,7 +3159,7 @@ static int apply_binary(struct image *img, struct patch *patch)
                 * apply the patch data to it, which is stored
                 * in the patch->fragments->{patch,size}.
                 */
-               if (apply_binary_fragment(img, patch))
+               if (apply_binary_fragment(state, img, patch))
                        return error(_("binary patch does not apply to '%s'"),
                                     name);
 
@@ -3043,7 +3173,7 @@ static int apply_binary(struct image *img, struct patch *patch)
        return 0;
 }
 
-static int apply_fragments(struct image *img, struct patch *patch)
+static int apply_fragments(struct apply_state *state, struct image *img, struct patch *patch)
 {
        struct fragment *frag = patch->fragments;
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
@@ -3052,13 +3182,13 @@ static int apply_fragments(struct image *img, struct patch *patch)
        int nth = 0;
 
        if (patch->is_binary)
-               return apply_binary(img, patch);
+               return apply_binary(state, img, patch);
 
        while (frag) {
                nth++;
-               if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) {
+               if (apply_one_fragment(state, img, frag, inaccurate_eof, ws_rule, nth)) {
                        error(_("patch failed: %s:%ld"), name, frag->oldpos);
-                       if (!apply_with_reject)
+                       if (!state->apply_with_reject)
                                return -1;
                        frag->rejected = 1;
                }
@@ -3093,14 +3223,14 @@ static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf
        return read_blob_object(buf, ce->sha1, ce->ce_mode);
 }
 
-static struct patch *in_fn_table(const char *name)
+static struct patch *in_fn_table(struct apply_state *state, const char *name)
 {
        struct string_list_item *item;
 
        if (name == NULL)
                return NULL;
 
-       item = string_list_lookup(&fn_table, name);
+       item = string_list_lookup(&state->fn_table, name);
        if (item != NULL)
                return (struct patch *)item->util;
 
@@ -3132,7 +3262,7 @@ static int was_deleted(struct patch *patch)
        return patch == PATH_WAS_DELETED;
 }
 
-static void add_to_fn_table(struct patch *patch)
+static void add_to_fn_table(struct apply_state *state, struct patch *patch)
 {
        struct string_list_item *item;
 
@@ -3142,7 +3272,7 @@ static void add_to_fn_table(struct patch *patch)
         * file creations and copies
         */
        if (patch->new_name != NULL) {
-               item = string_list_insert(&fn_table, patch->new_name);
+               item = string_list_insert(&state->fn_table, patch->new_name);
                item->util = patch;
        }
 
@@ -3151,12 +3281,12 @@ static void add_to_fn_table(struct patch *patch)
         * later chunks shouldn't patch old names
         */
        if ((patch->new_name == NULL) || (patch->is_rename)) {
-               item = string_list_insert(&fn_table, patch->old_name);
+               item = string_list_insert(&state->fn_table, patch->old_name);
                item->util = PATH_WAS_DELETED;
        }
 }
 
-static void prepare_fn_table(struct patch *patch)
+static void prepare_fn_table(struct apply_state *state, struct patch *patch)
 {
        /*
         * store information about incoming file deletion
@@ -3164,7 +3294,7 @@ static void prepare_fn_table(struct patch *patch)
        while (patch) {
                if ((patch->new_name == NULL) || (patch->is_rename)) {
                        struct string_list_item *item;
-                       item = string_list_insert(&fn_table, patch->old_name);
+                       item = string_list_insert(&state->fn_table, patch->old_name);
                        item->util = PATH_TO_BE_DELETED;
                }
                patch = patch->next;
@@ -3185,7 +3315,9 @@ static int checkout_target(struct index_state *istate,
        return 0;
 }
 
-static struct patch *previous_patch(struct patch *patch, int *gone)
+static struct patch *previous_patch(struct apply_state *state,
+                                   struct patch *patch,
+                                   int *gone)
 {
        struct patch *previous;
 
@@ -3193,7 +3325,7 @@ static struct patch *previous_patch(struct patch *patch, int *gone)
        if (patch->is_copy || patch->is_rename)
                return NULL; /* "git" patches do not depend on the order */
 
-       previous = in_fn_table(patch->old_name);
+       previous = in_fn_table(state, patch->old_name);
        if (!previous)
                return NULL;
 
@@ -3218,13 +3350,14 @@ static int verify_index_match(const struct cache_entry *ce, struct stat *st)
 
 #define SUBMODULE_PATCH_WITHOUT_INDEX 1
 
-static int load_patch_target(struct strbuf *buf,
+static int load_patch_target(struct apply_state *state,
+                            struct strbuf *buf,
                             const struct cache_entry *ce,
                             struct stat *st,
                             const char *name,
                             unsigned expected_mode)
 {
-       if (cached || check_index) {
+       if (state->cached || state->check_index) {
                if (read_file_or_gitlink(ce, buf))
                        return error(_("read of %s failed"), name);
        } else if (name) {
@@ -3250,7 +3383,8 @@ static int load_patch_target(struct strbuf *buf,
  * applying a non-git patch that incrementally updates the tree,
  * we read from the result of a previous diff.
  */
-static int load_preimage(struct image *image,
+static int load_preimage(struct apply_state *state,
+                        struct image *image,
                         struct patch *patch, struct stat *st,
                         const struct cache_entry *ce)
 {
@@ -3260,7 +3394,7 @@ static int load_preimage(struct image *image,
        struct patch *previous;
        int status;
 
-       previous = previous_patch(patch, &status);
+       previous = previous_patch(state, patch, &status);
        if (status)
                return error(_("path %s has been renamed/deleted"),
                             patch->old_name);
@@ -3268,7 +3402,7 @@ static int load_preimage(struct image *image,
                /* We have a patched copy in memory; use that. */
                strbuf_add(&buf, previous->result, previous->resultsize);
        } else {
-               status = load_patch_target(&buf, ce, st,
+               status = load_patch_target(state, &buf, ce, st,
                                           patch->old_name, patch->old_mode);
                if (status < 0)
                        return status;
@@ -3327,7 +3461,9 @@ static int three_way_merge(struct image *image,
  * the current contents of the new_name.  In no cases other than that
  * this function will be called.
  */
-static int load_current(struct image *image, struct patch *patch)
+static int load_current(struct apply_state *state,
+                       struct image *image,
+                       struct patch *patch)
 {
        struct strbuf buf = STRBUF_INIT;
        int status, pos;
@@ -3354,7 +3490,7 @@ static int load_current(struct image *image, struct patch *patch)
        if (verify_index_match(ce, &st))
                return error(_("%s: does not match index"), name);
 
-       status = load_patch_target(&buf, ce, &st, name, mode);
+       status = load_patch_target(state, &buf, ce, &st, name, mode);
        if (status < 0)
                return status;
        else if (status)
@@ -3364,8 +3500,11 @@ static int load_current(struct image *image, struct patch *patch)
        return 0;
 }
 
-static int try_threeway(struct image *image, struct patch *patch,
-                       struct stat *st, const struct cache_entry *ce)
+static int try_threeway(struct apply_state *state,
+                       struct image *image,
+                       struct patch *patch,
+                       struct stat *st,
+                       const struct cache_entry *ce)
 {
        unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
        struct strbuf buf = STRBUF_INIT;
@@ -3391,7 +3530,7 @@ static int try_threeway(struct image *image, struct patch *patch,
        img = strbuf_detach(&buf, &len);
        prepare_image(&tmp_image, img, len, 1);
        /* Apply the patch to get the post image */
-       if (apply_fragments(&tmp_image, patch) < 0) {
+       if (apply_fragments(state, &tmp_image, patch) < 0) {
                clear_image(&tmp_image);
                return -1;
        }
@@ -3401,11 +3540,11 @@ static int try_threeway(struct image *image, struct patch *patch,
 
        /* our_sha1[] is ours */
        if (patch->is_new) {
-               if (load_current(&tmp_image, patch))
+               if (load_current(state, &tmp_image, patch))
                        return error("cannot read the current contents of '%s'",
                                     patch->new_name);
        } else {
-               if (load_preimage(&tmp_image, patch, st, ce))
+               if (load_preimage(state, &tmp_image, patch, st, ce))
                        return error("cannot read the current contents of '%s'",
                                     patch->old_name);
        }
@@ -3435,22 +3574,23 @@ static int try_threeway(struct image *image, struct patch *patch,
        return 0;
 }
 
-static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce)
+static int apply_data(struct apply_state *state, struct patch *patch,
+                     struct stat *st, const struct cache_entry *ce)
 {
        struct image image;
 
-       if (load_preimage(&image, patch, st, ce) < 0)
+       if (load_preimage(state, &image, patch, st, ce) < 0)
                return -1;
 
        if (patch->direct_to_threeway ||
-           apply_fragments(&image, patch) < 0) {
+           apply_fragments(state, &image, patch) < 0) {
                /* Note: with --reject, apply_fragments() returns 0 */
-               if (!threeway || try_threeway(&image, patch, st, ce) < 0)
+               if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0)
                        return -1;
        }
        patch->result = image.buf;
        patch->resultsize = image.len;
-       add_to_fn_table(patch);
+       add_to_fn_table(state, patch);
        free(image.line_allocated);
 
        if (0 < patch->is_delete && patch->resultsize)
@@ -3470,7 +3610,10 @@ static int apply_data(struct patch *patch, struct stat *st, const struct cache_e
  * check_patch() separately makes sure (and errors out otherwise) that
  * the path the patch creates does not exist in the current tree.
  */
-static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
+static int check_preimage(struct apply_state *state,
+                         struct patch *patch,
+                         struct cache_entry **ce,
+                         struct stat *st)
 {
        const char *old_name = patch->old_name;
        struct patch *previous = NULL;
@@ -3481,19 +3624,19 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                return 0;
 
        assert(patch->is_new <= 0);
-       previous = previous_patch(patch, &status);
+       previous = previous_patch(state, patch, &status);
 
        if (status)
                return error(_("path %s has been renamed/deleted"), old_name);
        if (previous) {
                st_mode = previous->new_mode;
-       } else if (!cached) {
+       } else if (!state->cached) {
                stat_ret = lstat(old_name, st);
                if (stat_ret && errno != ENOENT)
                        return error(_("%s: %s"), old_name, strerror(errno));
        }
 
-       if (check_index && !previous) {
+       if (state->check_index && !previous) {
                int pos = cache_name_pos(old_name, strlen(old_name));
                if (pos < 0) {
                        if (patch->is_new < 0)
@@ -3505,9 +3648,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                        if (checkout_target(&the_index, *ce, st))
                                return -1;
                }
-               if (!cached && verify_index_match(*ce, st))
+               if (!state->cached && verify_index_match(*ce, st))
                        return error(_("%s: does not match index"), old_name);
-               if (cached)
+               if (state->cached)
                        st_mode = (*ce)->ce_mode;
        } else if (stat_ret < 0) {
                if (patch->is_new < 0)
@@ -3515,7 +3658,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                return error(_("%s: %s"), old_name, strerror(errno));
        }
 
-       if (!cached && !previous)
+       if (!state->cached && !previous)
                st_mode = ce_mode_from_stat(*ce, st->st_mode);
 
        if (patch->is_new < 0)
@@ -3543,15 +3686,17 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
 #define EXISTS_IN_INDEX 1
 #define EXISTS_IN_WORKTREE 2
 
-static int check_to_create(const char *new_name, int ok_if_exists)
+static int check_to_create(struct apply_state *state,
+                          const char *new_name,
+                          int ok_if_exists)
 {
        struct stat nst;
 
-       if (check_index &&
+       if (state->check_index &&
            cache_name_pos(new_name, strlen(new_name)) >= 0 &&
            !ok_if_exists)
                return EXISTS_IN_INDEX;
-       if (cached)
+       if (state->cached)
                return 0;
 
        if (!lstat(new_name, &nst)) {
@@ -3574,56 +3719,46 @@ static int check_to_create(const char *new_name, int ok_if_exists)
        return 0;
 }
 
-/*
- * We need to keep track of how symlinks in the preimage are
- * manipulated by the patches.  A patch to add a/b/c where a/b
- * is a symlink should not be allowed to affect the directory
- * the symlink points at, but if the same patch removes a/b,
- * it is perfectly fine, as the patch removes a/b to make room
- * to create a directory a/b so that a/b/c can be created.
- */
-static struct string_list symlink_changes;
-#define SYMLINK_GOES_AWAY 01
-#define SYMLINK_IN_RESULT 02
-
-static uintptr_t register_symlink_changes(const char *path, uintptr_t what)
+static uintptr_t register_symlink_changes(struct apply_state *state,
+                                         const char *path,
+                                         uintptr_t what)
 {
        struct string_list_item *ent;
 
-       ent = string_list_lookup(&symlink_changes, path);
+       ent = string_list_lookup(&state->symlink_changes, path);
        if (!ent) {
-               ent = string_list_insert(&symlink_changes, path);
+               ent = string_list_insert(&state->symlink_changes, path);
                ent->util = (void *)0;
        }
        ent->util = (void *)(what | ((uintptr_t)ent->util));
        return (uintptr_t)ent->util;
 }
 
-static uintptr_t check_symlink_changes(const char *path)
+static uintptr_t check_symlink_changes(struct apply_state *state, const char *path)
 {
        struct string_list_item *ent;
 
-       ent = string_list_lookup(&symlink_changes, path);
+       ent = string_list_lookup(&state->symlink_changes, path);
        if (!ent)
                return 0;
        return (uintptr_t)ent->util;
 }
 
-static void prepare_symlink_changes(struct patch *patch)
+static void prepare_symlink_changes(struct apply_state *state, struct patch *patch)
 {
        for ( ; patch; patch = patch->next) {
                if ((patch->old_name && S_ISLNK(patch->old_mode)) &&
                    (patch->is_rename || patch->is_delete))
                        /* the symlink at patch->old_name is removed */
-                       register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY);
+                       register_symlink_changes(state, patch->old_name, SYMLINK_GOES_AWAY);
 
                if (patch->new_name && S_ISLNK(patch->new_mode))
                        /* the symlink at patch->new_name is created or remains */
-                       register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT);
+                       register_symlink_changes(state, patch->new_name, SYMLINK_IN_RESULT);
        }
 }
 
-static int path_is_beyond_symlink_1(struct strbuf *name)
+static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *name)
 {
        do {
                unsigned int change;
@@ -3633,7 +3768,7 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
                if (!name->len)
                        break;
                name->buf[name->len] = '\0';
-               change = check_symlink_changes(name->buf);
+               change = check_symlink_changes(state, name->buf);
                if (change & SYMLINK_IN_RESULT)
                        return 1;
                if (change & SYMLINK_GOES_AWAY)
@@ -3644,7 +3779,7 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
                        continue;
 
                /* otherwise, check the preimage */
-               if (check_index) {
+               if (state->check_index) {
                        struct cache_entry *ce;
 
                        ce = cache_file_exists(name->buf, name->len, ignore_case);
@@ -3659,14 +3794,14 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
        return 0;
 }
 
-static int path_is_beyond_symlink(const char *name_)
+static int path_is_beyond_symlink(struct apply_state *state, const char *name_)
 {
        int ret;
        struct strbuf name = STRBUF_INIT;
 
        assert(*name_ != '\0');
        strbuf_addstr(&name, name_);
-       ret = path_is_beyond_symlink_1(&name);
+       ret = path_is_beyond_symlink_1(state, &name);
        strbuf_release(&name);
 
        return ret;
@@ -3693,7 +3828,7 @@ static void die_on_unsafe_path(struct patch *patch)
  * Check and apply the patch in-core; leave the result in patch->result
  * for the caller to write it out to the final destination.
  */
-static int check_patch(struct patch *patch)
+static int check_patch(struct apply_state *state, struct patch *patch)
 {
        struct stat st;
        const char *old_name = patch->old_name;
@@ -3706,7 +3841,7 @@ static int check_patch(struct patch *patch)
 
        patch->rejected = 1; /* we will drop this after we succeed */
 
-       status = check_preimage(patch, &ce, &st);
+       status = check_preimage(state, patch, &ce, &st);
        if (status)
                return status;
        old_name = patch->old_name;
@@ -3725,7 +3860,7 @@ static int check_patch(struct patch *patch)
         * B and rename from A to B is handled the same way by asking
         * was_deleted().
         */
-       if ((tpatch = in_fn_table(new_name)) &&
+       if ((tpatch = in_fn_table(state, new_name)) &&
            (was_deleted(tpatch) || to_be_deleted(tpatch)))
                ok_if_exists = 1;
        else
@@ -3733,9 +3868,9 @@ static int check_patch(struct patch *patch)
 
        if (new_name &&
            ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) {
-               int err = check_to_create(new_name, ok_if_exists);
+               int err = check_to_create(state, new_name, ok_if_exists);
 
-               if (err && threeway) {
+               if (err && state->threeway) {
                        patch->direct_to_threeway = 1;
                } else switch (err) {
                case 0:
@@ -3776,7 +3911,7 @@ static int check_patch(struct patch *patch)
                }
        }
 
-       if (!unsafe_paths)
+       if (!state->unsafe_paths)
                die_on_unsafe_path(patch);
 
        /*
@@ -3788,27 +3923,27 @@ static int check_patch(struct patch *patch)
         * is not deposited to a path that is beyond a symbolic link
         * here.
         */
-       if (!patch->is_delete && path_is_beyond_symlink(patch->new_name))
+       if (!patch->is_delete && path_is_beyond_symlink(state, patch->new_name))
                return error(_("affected file '%s' is beyond a symbolic link"),
                             patch->new_name);
 
-       if (apply_data(patch, &st, ce) < 0)
+       if (apply_data(state, patch, &st, ce) < 0)
                return error(_("%s: patch does not apply"), name);
        patch->rejected = 0;
        return 0;
 }
 
-static int check_patch_list(struct patch *patch)
+static int check_patch_list(struct apply_state *state, struct patch *patch)
 {
        int err = 0;
 
-       prepare_symlink_changes(patch);
-       prepare_fn_table(patch);
+       prepare_symlink_changes(state, patch);
+       prepare_fn_table(state, patch);
        while (patch) {
-               if (apply_verbosely)
+               if (state->apply_verbosely)
                        say_patch_name(stderr,
                                       _("Checking patch %s..."), patch);
-               err |= check_patch(patch);
+               err |= check_patch(state, patch);
                patch = patch->next;
        }
        return err;
@@ -3912,7 +4047,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
        discard_index(&result);
 }
 
-static void stat_patch_list(struct patch *patch)
+static void stat_patch_list(struct apply_state *state, struct patch *patch)
 {
        int files, adds, dels;
 
@@ -3920,13 +4055,14 @@ static void stat_patch_list(struct patch *patch)
                files++;
                adds += patch->lines_added;
                dels += patch->lines_deleted;
-               show_stats(patch);
+               show_stats(state, patch);
        }
 
        print_stat_summary(stdout, files, adds, dels);
 }
 
-static void numstat_patch_list(struct patch *patch)
+static void numstat_patch_list(struct apply_state *state,
+                              struct patch *patch)
 {
        for ( ; patch; patch = patch->next) {
                const char *name;
@@ -3935,7 +4071,7 @@ static void numstat_patch_list(struct patch *patch)
                        printf("-\t-\t");
                else
                        printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
-               write_name_quoted(name, stdout, line_termination);
+               write_name_quoted(name, stdout, state->line_termination);
        }
 }
 
@@ -4017,49 +4153,53 @@ static void summary_patch_list(struct patch *patch)
        }
 }
 
-static void patch_stats(struct patch *patch)
+static void patch_stats(struct apply_state *state, struct patch *patch)
 {
        int lines = patch->lines_added + patch->lines_deleted;
 
-       if (lines > max_change)
-               max_change = lines;
+       if (lines > state->max_change)
+               state->max_change = lines;
        if (patch->old_name) {
                int len = quote_c_style(patch->old_name, NULL, NULL, 0);
                if (!len)
                        len = strlen(patch->old_name);
-               if (len > max_len)
-                       max_len = len;
+               if (len > state->max_len)
+                       state->max_len = len;
        }
        if (patch->new_name) {
                int len = quote_c_style(patch->new_name, NULL, NULL, 0);
                if (!len)
                        len = strlen(patch->new_name);
-               if (len > max_len)
-                       max_len = len;
+               if (len > state->max_len)
+                       state->max_len = len;
        }
 }
 
-static void remove_file(struct patch *patch, int rmdir_empty)
+static void remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty)
 {
-       if (update_index) {
+       if (state->update_index) {
                if (remove_file_from_cache(patch->old_name) < 0)
                        die(_("unable to remove %s from index"), patch->old_name);
        }
-       if (!cached) {
+       if (!state->cached) {
                if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
                        remove_path(patch->old_name);
                }
        }
 }
 
-static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
+static void add_index_file(struct apply_state *state,
+                          const char *path,
+                          unsigned mode,
+                          void *buf,
+                          unsigned long size)
 {
        struct stat st;
        struct cache_entry *ce;
        int namelen = strlen(path);
        unsigned ce_size = cache_entry_size(namelen);
 
-       if (!update_index)
+       if (!state->update_index)
                return;
 
        ce = xcalloc(1, ce_size);
@@ -4074,7 +4214,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
                    get_sha1_hex(s, ce->sha1))
                        die(_("corrupt patch for submodule %s"), path);
        } else {
-               if (!cached) {
+               if (!state->cached) {
                        if (lstat(path, &st) < 0)
                                die_errno(_("unable to stat newly created file '%s'"),
                                          path);
@@ -4126,9 +4266,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
  * which is true 99% of the time anyway. If they don't,
  * we create them and try again.
  */
-static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
+static void create_one_file(struct apply_state *state,
+                           char *path,
+                           unsigned mode,
+                           const char *buf,
+                           unsigned long size)
 {
-       if (cached)
+       if (state->cached)
                return;
        if (!try_create_file(path, mode, buf, size))
                return;
@@ -4169,13 +4313,14 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
        die_errno(_("unable to write file '%s' mode %o"), path, mode);
 }
 
-static void add_conflicted_stages_file(struct patch *patch)
+static void add_conflicted_stages_file(struct apply_state *state,
+                                      struct patch *patch)
 {
        int stage, namelen;
        unsigned ce_size, mode;
        struct cache_entry *ce;
 
-       if (!update_index)
+       if (!state->update_index)
                return;
        namelen = strlen(patch->new_name);
        ce_size = cache_entry_size(namelen);
@@ -4196,7 +4341,7 @@ static void add_conflicted_stages_file(struct patch *patch)
        }
 }
 
-static void create_file(struct patch *patch)
+static void create_file(struct apply_state *state, struct patch *patch)
 {
        char *path = patch->new_name;
        unsigned mode = patch->new_mode;
@@ -4205,25 +4350,27 @@ static void create_file(struct patch *patch)
 
        if (!mode)
                mode = S_IFREG | 0644;
-       create_one_file(path, mode, buf, size);
+       create_one_file(state, path, mode, buf, size);
 
        if (patch->conflicted_threeway)
-               add_conflicted_stages_file(patch);
+               add_conflicted_stages_file(state, patch);
        else
-               add_index_file(path, mode, buf, size);
+               add_index_file(state, path, mode, buf, size);
 }
 
 /* phase zero is to remove, phase one is to create */
-static void write_out_one_result(struct patch *patch, int phase)
+static void write_out_one_result(struct apply_state *state,
+                                struct patch *patch,
+                                int phase)
 {
        if (patch->is_delete > 0) {
                if (phase == 0)
-                       remove_file(patch, 1);
+                       remove_file(state, patch, 1);
                return;
        }
        if (patch->is_new > 0 || patch->is_copy) {
                if (phase == 1)
-                       create_file(patch);
+                       create_file(state, patch);
                return;
        }
        /*
@@ -4231,12 +4378,12 @@ static void write_out_one_result(struct patch *patch, int phase)
         * thing: remove the old, write the new
         */
        if (phase == 0)
-               remove_file(patch, patch->is_rename);
+               remove_file(state, patch, patch->is_rename);
        if (phase == 1)
-               create_file(patch);
+               create_file(state, patch);
 }
 
-static int write_out_one_reject(struct patch *patch)
+static int write_out_one_reject(struct apply_state *state, struct patch *patch)
 {
        FILE *rej;
        char namebuf[PATH_MAX];
@@ -4251,7 +4398,7 @@ static int write_out_one_reject(struct patch *patch)
        }
 
        if (!cnt) {
-               if (apply_verbosely)
+               if (state->apply_verbosely)
                        say_patch_name(stderr,
                                       _("Applied patch %s cleanly."), patch);
                return 0;
@@ -4307,7 +4454,7 @@ static int write_out_one_reject(struct patch *patch)
        return -1;
 }
 
-static int write_out_results(struct patch *list)
+static int write_out_results(struct apply_state *state, struct patch *list)
 {
        int phase;
        int errs = 0;
@@ -4320,9 +4467,9 @@ static int write_out_results(struct patch *list)
                        if (l->rejected)
                                errs = 1;
                        else {
-                               write_out_one_result(l, phase);
+                               write_out_one_result(state, l, phase);
                                if (phase == 1) {
-                                       if (write_out_one_reject(l))
+                                       if (write_out_one_reject(state, l))
                                                errs = 1;
                                        if (l->conflicted_threeway) {
                                                string_list_append(&cpath, l->new_name);
@@ -4353,14 +4500,17 @@ static struct lock_file lock_file;
 #define INACCURATE_EOF (1<<0)
 #define RECOUNT                (1<<1)
 
-static int apply_patch(int fd, const char *filename, int options)
+static int apply_patch(struct apply_state *state,
+                      int fd,
+                      const char *filename,
+                      int options)
 {
        size_t offset;
        struct strbuf buf = STRBUF_INIT; /* owns the patch text */
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
 
-       patch_input_file = filename;
+       state->patch_input_file = filename;
        read_patch_file(&buf, fd);
        offset = 0;
        while (offset < buf.len) {
@@ -4370,20 +4520,20 @@ static int apply_patch(int fd, const char *filename, int options)
                patch = xcalloc(1, sizeof(*patch));
                patch->inaccurate_eof = !!(options & INACCURATE_EOF);
                patch->recount =  !!(options & RECOUNT);
-               nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
+               nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch);
                if (nr < 0) {
                        free_patch(patch);
                        break;
                }
-               if (apply_in_reverse)
+               if (state->apply_in_reverse)
                        reverse_patches(patch);
-               if (use_patch(patch)) {
-                       patch_stats(patch);
+               if (use_patch(state, patch)) {
+                       patch_stats(state, patch);
                        *listp = patch;
                        listp = &patch->next;
                }
                else {
-                       if (apply_verbosely)
+                       if (state->apply_verbosely)
                                say_patch_name(stderr, _("Skipped patch '%s'."), patch);
                        free_patch(patch);
                        skipped_patch++;
@@ -4394,45 +4544,45 @@ static int apply_patch(int fd, const char *filename, int options)
        if (!list && !skipped_patch)
                die(_("unrecognized input"));
 
-       if (whitespace_error && (ws_error_action == die_on_ws_error))
-               apply = 0;
+       if (state->whitespace_error && (state->ws_error_action == die_on_ws_error))
+               state->apply = 0;
 
-       update_index = check_index && apply;
-       if (update_index && newfd < 0)
-               newfd = hold_locked_index(&lock_file, 1);
+       state->update_index = state->check_index && state->apply;
+       if (state->update_index && state->newfd < 0)
+               state->newfd = hold_locked_index(state->lock_file, 1);
 
-       if (check_index) {
+       if (state->check_index) {
                if (read_cache() < 0)
                        die(_("unable to read index file"));
        }
 
-       if ((check || apply) &&
-           check_patch_list(list) < 0 &&
-           !apply_with_reject)
+       if ((state->check || state->apply) &&
+           check_patch_list(state, list) < 0 &&
+           !state->apply_with_reject)
                exit(1);
 
-       if (apply && write_out_results(list)) {
-               if (apply_with_reject)
+       if (state->apply && write_out_results(state, list)) {
+               if (state->apply_with_reject)
                        exit(1);
                /* with --3way, we still need to write the index out */
                return 1;
        }
 
-       if (fake_ancestor)
-               build_fake_ancestor(list, fake_ancestor);
+       if (state->fake_ancestor)
+               build_fake_ancestor(list, state->fake_ancestor);
 
-       if (diffstat)
-               stat_patch_list(list);
+       if (state->diffstat)
+               stat_patch_list(state, list);
 
-       if (numstat)
-               numstat_patch_list(list);
+       if (state->numstat)
+               numstat_patch_list(state, list);
 
-       if (summary)
+       if (state->summary)
                summary_patch_list(list);
 
        free_patch_list(list);
        strbuf_release(&buf);
-       string_list_clear(&fn_table, 0);
+       string_list_clear(&state->fn_table, 0);
        return 0;
 }
 
@@ -4446,220 +4596,284 @@ static void git_apply_config(void)
 static int option_parse_exclude(const struct option *opt,
                                const char *arg, int unset)
 {
-       add_name_limit(arg, 1);
+       struct apply_state *state = opt->value;
+       add_name_limit(state, arg, 1);
        return 0;
 }
 
 static int option_parse_include(const struct option *opt,
                                const char *arg, int unset)
 {
-       add_name_limit(arg, 0);
-       has_include = 1;
+       struct apply_state *state = opt->value;
+       add_name_limit(state, arg, 0);
+       state->has_include = 1;
        return 0;
 }
 
 static int option_parse_p(const struct option *opt,
-                         const char *arg, int unset)
+                         const char *arg,
+                         int unset)
 {
-       p_value = atoi(arg);
-       p_value_known = 1;
+       struct apply_state *state = opt->value;
+       state->p_value = atoi(arg);
+       state->p_value_known = 1;
        return 0;
 }
 
 static int option_parse_space_change(const struct option *opt,
-                         const char *arg, int unset)
+                                    const char *arg, int unset)
 {
+       struct apply_state *state = opt->value;
        if (unset)
-               ws_ignore_action = ignore_ws_none;
+               state->ws_ignore_action = ignore_ws_none;
        else
-               ws_ignore_action = ignore_ws_change;
+               state->ws_ignore_action = ignore_ws_change;
        return 0;
 }
 
 static int option_parse_whitespace(const struct option *opt,
                                   const char *arg, int unset)
 {
-       const char **whitespace_option = opt->value;
-
-       *whitespace_option = arg;
-       parse_whitespace_option(arg);
+       struct apply_state *state = opt->value;
+       state->whitespace_option = arg;
+       parse_whitespace_option(state, arg);
        return 0;
 }
 
 static int option_parse_directory(const struct option *opt,
                                  const char *arg, int unset)
 {
-       strbuf_reset(&root);
-       strbuf_addstr(&root, arg);
-       strbuf_complete(&root, '/');
+       struct apply_state *state = opt->value;
+       strbuf_reset(&state->root);
+       strbuf_addstr(&state->root, arg);
+       strbuf_complete(&state->root, '/');
        return 0;
 }
 
-int cmd_apply(int argc, const char **argv, const char *prefix_)
+static void init_apply_state(struct apply_state *state,
+                            const char *prefix,
+                            struct lock_file *lock_file)
+{
+       memset(state, 0, sizeof(*state));
+       state->prefix = prefix;
+       state->prefix_length = state->prefix ? strlen(state->prefix) : 0;
+       state->lock_file = lock_file;
+       state->newfd = -1;
+       state->apply = 1;
+       state->line_termination = '\n';
+       state->p_value = 1;
+       state->p_context = UINT_MAX;
+       state->squelch_whitespace_errors = 5;
+       state->ws_error_action = warn_on_ws_error;
+       state->ws_ignore_action = ignore_ws_none;
+       state->linenr = 1;
+       string_list_init(&state->fn_table, 0);
+       string_list_init(&state->limit_by_name, 0);
+       string_list_init(&state->symlink_changes, 0);
+       strbuf_init(&state->root, 0);
+
+       git_apply_config();
+       if (apply_default_whitespace)
+               parse_whitespace_option(state, apply_default_whitespace);
+       if (apply_default_ignorewhitespace)
+               parse_ignorewhitespace_option(state, apply_default_ignorewhitespace);
+}
+
+static void clear_apply_state(struct apply_state *state)
+{
+       string_list_clear(&state->limit_by_name, 0);
+       string_list_clear(&state->symlink_changes, 0);
+       strbuf_release(&state->root);
+
+       /* &state->fn_table is cleared at the end of apply_patch() */
+}
+
+static void check_apply_state(struct apply_state *state, int force_apply)
+{
+       int is_not_gitdir = !startup_info->have_repository;
+
+       if (state->apply_with_reject && state->threeway)
+               die("--reject and --3way cannot be used together.");
+       if (state->cached && state->threeway)
+               die("--cached and --3way cannot be used together.");
+       if (state->threeway) {
+               if (is_not_gitdir)
+                       die(_("--3way outside a repository"));
+               state->check_index = 1;
+       }
+       if (state->apply_with_reject)
+               state->apply = state->apply_verbosely = 1;
+       if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor))
+               state->apply = 0;
+       if (state->check_index && is_not_gitdir)
+               die(_("--index outside a repository"));
+       if (state->cached) {
+               if (is_not_gitdir)
+                       die(_("--cached outside a repository"));
+               state->check_index = 1;
+       }
+       if (state->check_index)
+               state->unsafe_paths = 0;
+       if (!state->lock_file)
+               die("BUG: state->lock_file should not be NULL");
+}
+
+static int apply_all_patches(struct apply_state *state,
+                            int argc,
+                            const char **argv,
+                            int options)
 {
        int i;
        int errs = 0;
-       int is_not_gitdir = !startup_info->have_repository;
-       int force_apply = 0;
+       int read_stdin = 1;
+
+       for (i = 0; i < argc; i++) {
+               const char *arg = argv[i];
+               int fd;
+
+               if (!strcmp(arg, "-")) {
+                       errs |= apply_patch(state, 0, "<stdin>", options);
+                       read_stdin = 0;
+                       continue;
+               } else if (0 < state->prefix_length)
+                       arg = prefix_filename(state->prefix,
+                                             state->prefix_length,
+                                             arg);
+
+               fd = open(arg, O_RDONLY);
+               if (fd < 0)
+                       die_errno(_("can't open patch '%s'"), arg);
+               read_stdin = 0;
+               set_default_whitespace_mode(state);
+               errs |= apply_patch(state, fd, arg, options);
+               close(fd);
+       }
+       set_default_whitespace_mode(state);
+       if (read_stdin)
+               errs |= apply_patch(state, 0, "<stdin>", options);
+
+       if (state->whitespace_error) {
+               if (state->squelch_whitespace_errors &&
+                   state->squelch_whitespace_errors < state->whitespace_error) {
+                       int squelched =
+                               state->whitespace_error - state->squelch_whitespace_errors;
+                       warning(Q_("squelched %d whitespace error",
+                                  "squelched %d whitespace errors",
+                                  squelched),
+                               squelched);
+               }
+               if (state->ws_error_action == die_on_ws_error)
+                       die(Q_("%d line adds whitespace errors.",
+                              "%d lines add whitespace errors.",
+                              state->whitespace_error),
+                           state->whitespace_error);
+               if (state->applied_after_fixing_ws && state->apply)
+                       warning("%d line%s applied after"
+                               " fixing whitespace errors.",
+                               state->applied_after_fixing_ws,
+                               state->applied_after_fixing_ws == 1 ? "" : "s");
+               else if (state->whitespace_error)
+                       warning(Q_("%d line adds whitespace errors.",
+                                  "%d lines add whitespace errors.",
+                                  state->whitespace_error),
+                               state->whitespace_error);
+       }
+
+       if (state->update_index) {
+               if (write_locked_index(&the_index, state->lock_file, COMMIT_LOCK))
+                       die(_("Unable to write new index file"));
+               state->newfd = -1;
+       }
+
+       return !!errs;
+}
 
-       const char *whitespace_option = NULL;
+int cmd_apply(int argc, const char **argv, const char *prefix)
+{
+       int force_apply = 0;
+       int options = 0;
+       int ret;
+       struct apply_state state;
 
        struct option builtin_apply_options[] = {
-               { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"),
+               { OPTION_CALLBACK, 0, "exclude", &state, N_("path"),
                        N_("don't apply changes matching the given path"),
                        0, option_parse_exclude },
-               { OPTION_CALLBACK, 0, "include", NULL, N_("path"),
+               { OPTION_CALLBACK, 0, "include", &state, N_("path"),
                        N_("apply changes matching the given path"),
                        0, option_parse_include },
-               { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"),
+               { OPTION_CALLBACK, 'p', NULL, &state, N_("num"),
                        N_("remove <num> leading slashes from traditional diff paths"),
                        0, option_parse_p },
-               OPT_BOOL(0, "no-add", &no_add,
+               OPT_BOOL(0, "no-add", &state.no_add,
                        N_("ignore additions made by the patch")),
-               OPT_BOOL(0, "stat", &diffstat,
+               OPT_BOOL(0, "stat", &state.diffstat,
                        N_("instead of applying the patch, output diffstat for the input")),
                OPT_NOOP_NOARG(0, "allow-binary-replacement"),
                OPT_NOOP_NOARG(0, "binary"),
-               OPT_BOOL(0, "numstat", &numstat,
+               OPT_BOOL(0, "numstat", &state.numstat,
                        N_("show number of added and deleted lines in decimal notation")),
-               OPT_BOOL(0, "summary", &summary,
+               OPT_BOOL(0, "summary", &state.summary,
                        N_("instead of applying the patch, output a summary for the input")),
-               OPT_BOOL(0, "check", &check,
+               OPT_BOOL(0, "check", &state.check,
                        N_("instead of applying the patch, see if the patch is applicable")),
-               OPT_BOOL(0, "index", &check_index,
+               OPT_BOOL(0, "index", &state.check_index,
                        N_("make sure the patch is applicable to the current index")),
-               OPT_BOOL(0, "cached", &cached,
+               OPT_BOOL(0, "cached", &state.cached,
                        N_("apply a patch without touching the working tree")),
-               OPT_BOOL(0, "unsafe-paths", &unsafe_paths,
+               OPT_BOOL(0, "unsafe-paths", &state.unsafe_paths,
                        N_("accept a patch that touches outside the working area")),
                OPT_BOOL(0, "apply", &force_apply,
                        N_("also apply the patch (use with --stat/--summary/--check)")),
-               OPT_BOOL('3', "3way", &threeway,
+               OPT_BOOL('3', "3way", &state.threeway,
                         N_( "attempt three-way merge if a patch does not apply")),
-               OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
+               OPT_FILENAME(0, "build-fake-ancestor", &state.fake_ancestor,
                        N_("build a temporary index based on embedded index information")),
                /* Think twice before adding "--nul" synonym to this */
-               OPT_SET_INT('z', NULL, &line_termination,
+               OPT_SET_INT('z', NULL, &state.line_termination,
                        N_("paths are separated with NUL character"), '\0'),
-               OPT_INTEGER('C', NULL, &p_context,
+               OPT_INTEGER('C', NULL, &state.p_context,
                                N_("ensure at least <n> lines of context match")),
-               { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
+               { OPTION_CALLBACK, 0, "whitespace", &state, N_("action"),
                        N_("detect new or modified lines that have whitespace errors"),
                        0, option_parse_whitespace },
-               { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
+               { OPTION_CALLBACK, 0, "ignore-space-change", &state, NULL,
                        N_("ignore changes in whitespace when finding context"),
                        PARSE_OPT_NOARG, option_parse_space_change },
-               { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
+               { OPTION_CALLBACK, 0, "ignore-whitespace", &state, NULL,
                        N_("ignore changes in whitespace when finding context"),
                        PARSE_OPT_NOARG, option_parse_space_change },
-               OPT_BOOL('R', "reverse", &apply_in_reverse,
+               OPT_BOOL('R', "reverse", &state.apply_in_reverse,
                        N_("apply the patch in reverse")),
-               OPT_BOOL(0, "unidiff-zero", &unidiff_zero,
+               OPT_BOOL(0, "unidiff-zero", &state.unidiff_zero,
                        N_("don't expect at least one line of context")),
-               OPT_BOOL(0, "reject", &apply_with_reject,
+               OPT_BOOL(0, "reject", &state.apply_with_reject,
                        N_("leave the rejected hunks in corresponding *.rej files")),
-               OPT_BOOL(0, "allow-overlap", &allow_overlap,
+               OPT_BOOL(0, "allow-overlap", &state.allow_overlap,
                        N_("allow overlapping hunks")),
-               OPT__VERBOSE(&apply_verbosely, N_("be verbose")),
+               OPT__VERBOSE(&state.apply_verbosely, N_("be verbose")),
                OPT_BIT(0, "inaccurate-eof", &options,
                        N_("tolerate incorrectly detected missing new-line at the end of file"),
                        INACCURATE_EOF),
                OPT_BIT(0, "recount", &options,
                        N_("do not trust the line counts in the hunk headers"),
                        RECOUNT),
-               { OPTION_CALLBACK, 0, "directory", NULL, N_("root"),
+               { OPTION_CALLBACK, 0, "directory", &state, N_("root"),
                        N_("prepend <root> to all filenames"),
                        0, option_parse_directory },
                OPT_END()
        };
 
-       prefix = prefix_;
-       prefix_length = prefix ? strlen(prefix) : 0;
-       git_apply_config();
-       if (apply_default_whitespace)
-               parse_whitespace_option(apply_default_whitespace);
-       if (apply_default_ignorewhitespace)
-               parse_ignorewhitespace_option(apply_default_ignorewhitespace);
+       init_apply_state(&state, prefix, &lock_file);
 
-       argc = parse_options(argc, argv, prefix, builtin_apply_options,
+       argc = parse_options(argc, argv, state.prefix, builtin_apply_options,
                        apply_usage, 0);
 
-       if (apply_with_reject && threeway)
-               die("--reject and --3way cannot be used together.");
-       if (cached && threeway)
-               die("--cached and --3way cannot be used together.");
-       if (threeway) {
-               if (is_not_gitdir)
-                       die(_("--3way outside a repository"));
-               check_index = 1;
-       }
-       if (apply_with_reject)
-               apply = apply_verbosely = 1;
-       if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
-               apply = 0;
-       if (check_index && is_not_gitdir)
-               die(_("--index outside a repository"));
-       if (cached) {
-               if (is_not_gitdir)
-                       die(_("--cached outside a repository"));
-               check_index = 1;
-       }
-       if (check_index)
-               unsafe_paths = 0;
+       check_apply_state(&state, force_apply);
 
-       for (i = 0; i < argc; i++) {
-               const char *arg = argv[i];
-               int fd;
+       ret = apply_all_patches(&state, argc, argv, options);
 
-               if (!strcmp(arg, "-")) {
-                       errs |= apply_patch(0, "<stdin>", options);
-                       read_stdin = 0;
-                       continue;
-               } else if (0 < prefix_length)
-                       arg = prefix_filename(prefix, prefix_length, arg);
+       clear_apply_state(&state);
 
-               fd = open(arg, O_RDONLY);
-               if (fd < 0)
-                       die_errno(_("can't open patch '%s'"), arg);
-               read_stdin = 0;
-               set_default_whitespace_mode(whitespace_option);
-               errs |= apply_patch(fd, arg, options);
-               close(fd);
-       }
-       set_default_whitespace_mode(whitespace_option);
-       if (read_stdin)
-               errs |= apply_patch(0, "<stdin>", options);
-       if (whitespace_error) {
-               if (squelch_whitespace_errors &&
-                   squelch_whitespace_errors < whitespace_error) {
-                       int squelched =
-                               whitespace_error - squelch_whitespace_errors;
-                       warning(Q_("squelched %d whitespace error",
-                                  "squelched %d whitespace errors",
-                                  squelched),
-                               squelched);
-               }
-               if (ws_error_action == die_on_ws_error)
-                       die(Q_("%d line adds whitespace errors.",
-                              "%d lines add whitespace errors.",
-                              whitespace_error),
-                           whitespace_error);
-               if (applied_after_fixing_ws && apply)
-                       warning("%d line%s applied after"
-                               " fixing whitespace errors.",
-                               applied_after_fixing_ws,
-                               applied_after_fixing_ws == 1 ? "" : "s");
-               else if (whitespace_error)
-                       warning(Q_("%d line adds whitespace errors.",
-                                  "%d lines add whitespace errors.",
-                                  whitespace_error),
-                               whitespace_error);
-       }
-
-       if (update_index) {
-               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
-                       die(_("Unable to write new index file"));
-       }
-
-       return !!errs;
+       return ret;
 }
index 7417edf6ef15bf4c0611c8e177e96d5734c98ac2..1e214bd4ec2161ff0a9620f6433f43a3407c4c9e 100644 (file)
@@ -56,7 +56,7 @@ static int show_progress;
 static struct date_mode blame_date_mode = { DATE_ISO8601 };
 static size_t blame_date_width;
 
-static struct string_list mailmap;
+static struct string_list mailmap = STRING_LIST_INIT_NODUP;
 
 #ifndef DEBUG
 #define DEBUG 0
@@ -609,7 +609,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                }
        }
        diff_flush(&diff_opts);
-       free_pathspec(&diff_opts.pathspec);
+       clear_pathspec(&diff_opts.pathspec);
        return porigin;
 }
 
@@ -651,7 +651,7 @@ static struct origin *find_rename(struct scoreboard *sb,
                }
        }
        diff_flush(&diff_opts);
-       free_pathspec(&diff_opts.pathspec);
+       clear_pathspec(&diff_opts.pathspec);
        return porigin;
 }
 
@@ -1343,7 +1343,7 @@ static void find_copy_in_parent(struct scoreboard *sb,
        } while (unblamed);
        target->suspects = reverse_blame(leftover, NULL);
        diff_flush(&diff_opts);
-       free_pathspec(&diff_opts.pathspec);
+       clear_pathspec(&diff_opts.pathspec);
 }
 
 /*
@@ -2520,12 +2520,12 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        enum object_type type;
        struct commit *final_commit = NULL;
 
-       static struct string_list range_list;
-       static int output_option = 0, opt = 0;
-       static int show_stats = 0;
-       static const char *revs_file = NULL;
-       static const char *contents_from = NULL;
-       static const struct option options[] = {
+       struct string_list range_list = STRING_LIST_INIT_NODUP;
+       int output_option = 0, opt = 0;
+       int show_stats = 0;
+       const char *revs_file = NULL;
+       const char *contents_from = NULL;
+       const struct option options[] = {
                OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
                OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
                OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
index 8f7db98af65bd104c9da96a6cb007a745f79b8a4..31ea247e3f262fb19595905869a7f2d874aa898e 100644 (file)
@@ -49,8 +49,8 @@ static char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
 static enum transport_family family;
-static struct string_list option_config;
-static struct string_list option_reference;
+static struct string_list option_config = STRING_LIST_INIT_NODUP;
+static struct string_list option_reference = STRING_LIST_INIT_NODUP;
 static int option_dissociate;
 static int max_jobs = -1;
 
index 163dbcabf3ba25a6e0e7f387624cee9a2edbb149..1f6dbcd0d06a54eadc0d99a676adf3a9203aca61 100644 (file)
@@ -92,8 +92,9 @@ N_("If you wish to skip this commit, use:\n"
 "Then \"git cherry-pick --continue\" will resume cherry-picking\n"
 "the remaining commits.\n");
 
+static GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
+
 static const char *use_message_buffer;
-static const char commit_editmsg[] = "COMMIT_EDITMSG";
 static struct lock_file index_lock; /* real index */
 static struct lock_file false_lock; /* used only for partial commits */
 static enum {
@@ -714,7 +715,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                char *buffer;
                buffer = strstr(use_message_buffer, "\n\n");
                if (buffer)
-                       strbuf_addstr(&sb, buffer + 2);
+                       strbuf_addstr(&sb, skip_blank_lines(buffer + 2));
                hook_arg1 = "commit";
                hook_arg2 = use_message;
        } else if (fixup_message) {
@@ -772,9 +773,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                hook_arg2 = "";
        }
 
-       s->fp = fopen_for_writing(git_path(commit_editmsg));
+       s->fp = fopen_for_writing(git_path_commit_editmsg());
        if (s->fp == NULL)
-               die_errno(_("could not open '%s'"), git_path(commit_editmsg));
+               die_errno(_("could not open '%s'"), git_path_commit_editmsg());
 
        /* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */
        old_display_comment_prefix = s->display_comment_prefix;
@@ -951,7 +952,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        }
 
        if (run_commit_hook(use_editor, index_file, "prepare-commit-msg",
-                           git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
+                           git_path_commit_editmsg(), hook_arg1, hook_arg2, NULL))
                return 0;
 
        if (use_editor) {
@@ -959,7 +960,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                const char *env[2] = { NULL };
                env[0] =  index;
                snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
-               if (launch_editor(git_path(commit_editmsg), NULL, env)) {
+               if (launch_editor(git_path_commit_editmsg(), NULL, env)) {
                        fprintf(stderr,
                        _("Please supply the message using either -m or -F option.\n"));
                        exit(1);
@@ -967,7 +968,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        }
 
        if (!no_verify &&
-           run_commit_hook(use_editor, index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
+           run_commit_hook(use_editor, index_file, "commit-msg", git_path_commit_editmsg(), NULL)) {
                return 0;
        }
 
@@ -1738,7 +1739,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        /* Finally, get the commit message */
        strbuf_reset(&sb);
-       if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
+       if (strbuf_read_file(&sb, git_path_commit_editmsg(), 0) < 0) {
                int saved_errno = errno;
                rollback_index_files();
                die(_("could not read commit message: %s"), strerror(saved_errno));
index b99ae4be8875ad8fb41fd8be2a84b6191681a4e4..175f14797b101d22ead9d1008744440da66a7c1c 100644 (file)
@@ -20,7 +20,7 @@ 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_DUP;
+       struct string_list trailers = STRING_LIST_INIT_NODUP;
 
        struct option options[] = {
                OPT_BOOL(0, "in-place", &in_place, N_("edit files in place")),
index 099f4f7be92c6d4371dcc8cba1322351c6840439..0b6f7392b9fac31fe08f3e8743b44d018f6d4740 100644 (file)
@@ -33,6 +33,7 @@ static const char *default_date_mode = NULL;
 static int default_abbrev_commit;
 static int default_show_root = 1;
 static int default_follow;
+static int default_show_signature;
 static int decoration_style;
 static int decoration_given;
 static int use_mailmap_config;
@@ -119,6 +120,7 @@ static void cmd_log_init_defaults(struct rev_info *rev)
        rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
+       rev->show_signature = default_show_signature;
        DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
 
        if (default_date_mode)
@@ -409,6 +411,10 @@ static int git_log_config(const char *var, const char *value, void *cb)
                use_mailmap_config = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "log.showsignature")) {
+               default_show_signature = git_config_bool(var, value);
+               return 0;
+       }
 
        if (grep_config(var, value, cb) < 0)
                return -1;
@@ -674,9 +680,9 @@ static int auto_number = 1;
 
 static char *default_attach = NULL;
 
-static struct string_list extra_hdr;
-static struct string_list extra_to;
-static struct string_list extra_cc;
+static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
+static struct string_list extra_to = STRING_LIST_INIT_NODUP;
+static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
 
 static void add_header(const char *value)
 {
@@ -953,7 +959,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        struct pretty_print_context pp = {0};
        struct commit *head = list[0];
 
-       if (rev->commit_format != CMIT_FMT_EMAIL)
+       if (!cmit_fmt_is_mail(rev->commit_format))
                die(_("Cover letter needs email format"));
 
        committer = git_committer_info(0);
index 4859ede38adc76521baf97326b82a007a37b3893..30681681c1389826513464385405dfe6ef8a2855 100644 (file)
@@ -45,6 +45,19 @@ static int is_from_line(const char *line, int len)
 
 static struct strbuf buf = STRBUF_INIT;
 static int keep_cr;
+static int mboxrd;
+
+static int is_gtfrom(const struct strbuf *buf)
+{
+       size_t min = strlen(">From ");
+       size_t ngt;
+
+       if (buf->len < min)
+               return 0;
+
+       ngt = strspn(buf->buf, ">");
+       return ngt && starts_with(buf->buf + ngt, "From ");
+}
 
 /* Called with the first line (potentially partial)
  * already in buf[] -- normally that should begin with
@@ -77,6 +90,9 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
                        strbuf_addch(&buf, '\n');
                }
 
+               if (mboxrd && is_gtfrom(&buf))
+                       strbuf_remove(&buf, 0, 1);
+
                if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
                        die_errno("cannot write output");
 
@@ -271,6 +287,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
                        keep_cr = 1;
                } else if ( arg[1] == 'o' && arg[2] ) {
                        dir = arg+2;
+               } else if (!strcmp(arg, "--mboxrd")) {
+                       mboxrd = 1;
                } else if ( arg[1] == '-' && !arg[2] ) {
                        argp++; /* -- marks end of options */
                        break;
index 8f5e358e22b40ff2e162cc7037339ad357622650..a2f8cfdec0d4034c20f3307c390cec9dec6d4a18 100644 (file)
@@ -44,6 +44,7 @@ static int non_empty;
 static int reuse_delta = 1, reuse_object = 1;
 static int keep_unreachable, unpack_unreachable, include_tag;
 static unsigned long unpack_unreachable_expiration;
+static int pack_loose_unreachable;
 static int local;
 static int incremental;
 static int ignore_packed_keep;
@@ -2378,6 +2379,32 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
        free(in_pack.array);
 }
 
+static int add_loose_object(const unsigned char *sha1, const char *path,
+                           void *data)
+{
+       enum object_type type = sha1_object_info(sha1, NULL);
+
+       if (type < 0) {
+               warning("loose object at %s could not be examined", path);
+               return 0;
+       }
+
+       add_object_entry(sha1, type, "", 0);
+       return 0;
+}
+
+/*
+ * We actually don't even have to worry about reachability here.
+ * add_object_entry will weed out duplicates, so we just add every
+ * loose object we find.
+ */
+static void add_unreachable_loose_objects(void)
+{
+       for_each_loose_file_in_objdir(get_object_directory(),
+                                     add_loose_object,
+                                     NULL, NULL, NULL);
+}
+
 static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 {
        static struct packed_git *last_found = (void *)1;
@@ -2547,6 +2574,8 @@ static void get_object_list(int ac, const char **av)
 
        if (keep_unreachable)
                add_objects_in_unpacked_packs(&revs);
+       if (pack_loose_unreachable)
+               add_unreachable_loose_objects();
        if (unpack_unreachable)
                loosen_unused_packed_objects(&revs);
 
@@ -2647,6 +2676,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                         N_("include tag objects that refer to objects to be packed")),
                OPT_BOOL(0, "keep-unreachable", &keep_unreachable,
                         N_("keep unreachable objects")),
+               OPT_BOOL(0, "pack-loose-unreachable", &pack_loose_unreachable,
+                        N_("pack loose unreachable objects")),
                { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, N_("time"),
                  N_("unpack unreachable objects newer than <time>"),
                  PARSE_OPT_OPTARG, option_parse_unpack_unreachable },
index 1d7333c8a11f7aedf244c99f5ac67bea7fccaab4..897a7f4e4e040089b4021a9a0e217fbb63e7c6ec 100644 (file)
@@ -815,6 +815,9 @@ static int run_rebase(const unsigned char *curr_head,
                argv_array_push(&args, "--no-autostash");
        else if (opt_autostash == 1)
                argv_array_push(&args, "--autostash");
+       if (opt_verify_signatures &&
+           !strcmp(opt_verify_signatures, "--verify-signatures"))
+               warning(_("ignoring --verify-signatures for rebase"));
 
        argv_array_push(&args, "--onto");
        argv_array_push(&args, sha1_to_hex(merge_head));
index a744437b5876171250d4731d181f7b30e5444187..15c323a7cdc97bce04203d0edcc425e3d74fc7c7 100644 (file)
@@ -1775,9 +1775,20 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        const char *argv_gc_auto[] = {
                                "gc", "--auto", "--quiet", NULL,
                        };
-                       int opt = RUN_GIT_CMD | RUN_COMMAND_STDOUT_TO_STDERR;
+                       struct child_process proc = CHILD_PROCESS_INIT;
+
+                       proc.no_stdin = 1;
+                       proc.stdout_to_stderr = 1;
+                       proc.err = use_sideband ? -1 : 0;
+                       proc.git_cmd = 1;
+                       proc.argv = argv_gc_auto;
+
                        close_all_packs();
-                       run_command_v_opt(argv_gc_auto, opt);
+                       if (!start_command(&proc)) {
+                               if (use_sideband)
+                                       copy_to_sideband(proc.err, -1, NULL);
+                               finish_command(&proc);
+                       }
                }
                if (auto_update_server_info)
                        update_server_info(0);
index d33766be395ad637c4505f5ca9024a9b8331d99d..5ded3018afcf8c44fccac5062aa34df9ba013169 100644 (file)
@@ -247,7 +247,7 @@ struct branch_info {
        enum { NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE } rebase;
 };
 
-static struct string_list branch_list;
+static struct string_list branch_list = STRING_LIST_INIT_NODUP;
 
 static const char *abbrev_ref(const char *name, const char *prefix)
 {
index 858db38f52e878a41cefdd47bbafd1250c7483b0..f7b7409cb7f8e2d35b4428c82d87d7067020a398 100644 (file)
@@ -146,6 +146,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        int pack_everything = 0;
        int delete_redundant = 0;
        const char *unpack_unreachable = NULL;
+       int keep_unreachable = 0;
        const char *window = NULL, *window_memory = NULL;
        const char *depth = NULL;
        const char *max_pack_size = NULL;
@@ -175,6 +176,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                                N_("write bitmap index")),
                OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
                                N_("with -A, do not loosen objects older than this")),
+               OPT_BOOL('k', "keep-unreachable", &keep_unreachable,
+                               N_("with -a, repack unreachable objects")),
                OPT_STRING(0, "window", &window, N_("n"),
                                N_("size of the window used for delta compression")),
                OPT_STRING(0, "window-memory", &window_memory, N_("bytes"),
@@ -196,6 +199,10 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (delete_redundant && repository_format_precious_objects)
                die(_("cannot delete packs in a precious-objects repo"));
 
+       if (keep_unreachable &&
+           (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
+               die(_("--keep-unreachable and -A are incompatible"));
+
        if (pack_kept_objects < 0)
                pack_kept_objects = write_bitmaps;
 
@@ -239,6 +246,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                        } else if (pack_everything & LOOSEN_UNREACHABLE) {
                                argv_array_push(&cmd.args,
                                                "--unpack-unreachable");
+                       } else if (keep_unreachable) {
+                               argv_array_push(&cmd.args, "--keep-unreachable");
+                               argv_array_push(&cmd.args, "--pack-loose-unreachable");
                        } else {
                                argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
                        }
index 092c3a5399c731e83058233f8e3e459be493e140..5c6206bc1c40dd1ac7081896232910be95d13977 100644 (file)
@@ -103,7 +103,7 @@ static void print_new_head_line(struct commit *commit)
        if (body) {
                const char *eol;
                size_t len;
-               body += 2;
+               body = skip_blank_lines(body + 2);
                eol = strchr(body, '\n');
                len = eol ? eol - body : strlen(body);
                printf(" %.*s\n", (int) len, body);
@@ -158,7 +158,7 @@ static int read_from_tree(const struct pathspec *pathspec,
                return 1;
        diffcore_std(&opt);
        diff_flush(&opt);
-       free_pathspec(&opt.pathspec);
+       clear_pathspec(&opt.pathspec);
 
        return 0;
 }
index bfc082e58467953c1e4c96fd27a884abea4f5127..f83984e8a1264054b5bd32b3d559c75708a4f428 100644 (file)
@@ -233,11 +233,11 @@ void shortlog_init(struct shortlog *log)
 
 int cmd_shortlog(int argc, const char **argv, const char *prefix)
 {
-       static struct shortlog log;
-       static struct rev_info rev;
+       struct shortlog log = { STRING_LIST_INIT_NODUP };
+       struct rev_info rev;
        int nongit = !startup_info->have_repository;
 
-       static const struct option options[] = {
+       const struct option options[] = {
                OPT_BOOL('n', "numbered", &log.sort_by_number,
                         N_("sort output according to the number of commits per author")),
                OPT_BOOL('s', "summary", &log.summary,
index 926d2051623be390d5de2611d81d5fe9f39a8169..b22352b6e1e4c40d1a6e182f5c12abf6351f71fb 100644 (file)
@@ -579,6 +579,7 @@ struct submodule_update_clone {
 
        /* configuration parameters which are passed on to the children */
        int quiet;
+       int recommend_shallow;
        const char *reference;
        const char *depth;
        const char *recursive_prefix;
@@ -589,10 +590,14 @@ struct submodule_update_clone {
 
        /* If we want to stop as fast as possible and return an error */
        unsigned quickstop : 1;
+
+       /* failed clones to be retried again */
+       const struct cache_entry **failed_clones;
+       int failed_clones_nr, failed_clones_alloc;
 };
 #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
-       SUBMODULE_UPDATE_STRATEGY_INIT, 0, NULL, NULL, NULL, NULL, \
-       STRING_LIST_INIT_DUP, 0}
+       SUBMODULE_UPDATE_STRATEGY_INIT, 0, -1, NULL, NULL, NULL, NULL, \
+       STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
 
 
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@@ -696,6 +701,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                argv_array_push(&child->args, "--quiet");
        if (suc->prefix)
                argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
+       if (suc->recommend_shallow && sub->recommend_shallow == 1)
+               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", url, NULL);
@@ -715,23 +722,47 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 static int update_clone_get_next_task(struct child_process *child,
                                      struct strbuf *err,
                                      void *suc_cb,
-                                     void **void_task_cb)
+                                     void **idx_task_cb)
 {
        struct submodule_update_clone *suc = suc_cb;
+       const struct cache_entry *ce;
+       int index;
 
        for (; suc->current < suc->list.nr; suc->current++) {
-               const struct cache_entry *ce = suc->list.entries[suc->current];
+               ce = suc->list.entries[suc->current];
                if (prepare_to_clone_next_submodule(ce, child, suc, err)) {
+                       int *p = xmalloc(sizeof(*p));
+                       *p = suc->current;
+                       *idx_task_cb = p;
                        suc->current++;
                        return 1;
                }
        }
+
+       /*
+        * The loop above tried cloning each submodule once, now try the
+        * stragglers again, which we can imagine as an extension of the
+        * entry list.
+        */
+       index = suc->current - suc->list.nr;
+       if (index < suc->failed_clones_nr) {
+               int *p;
+               ce = suc->failed_clones[index];
+               if (!prepare_to_clone_next_submodule(ce, child, suc, err))
+                       die("BUG: ce was a submodule before?");
+               p = xmalloc(sizeof(*p));
+               *p = suc->current;
+               *idx_task_cb = p;
+               suc->current ++;
+               return 1;
+       }
+
        return 0;
 }
 
 static int update_clone_start_failure(struct strbuf *err,
                                      void *suc_cb,
-                                     void *void_task_cb)
+                                     void *idx_task_cb)
 {
        struct submodule_update_clone *suc = suc_cb;
        suc->quickstop = 1;
@@ -741,15 +772,39 @@ static int update_clone_start_failure(struct strbuf *err,
 static int update_clone_task_finished(int result,
                                      struct strbuf *err,
                                      void *suc_cb,
-                                     void *void_task_cb)
+                                     void *idx_task_cb)
 {
+       const struct cache_entry *ce;
        struct submodule_update_clone *suc = suc_cb;
 
+       int *idxP = *(int**)idx_task_cb;
+       int idx = *idxP;
+       free(idxP);
+
        if (!result)
                return 0;
 
-       suc->quickstop = 1;
-       return 1;
+       if (idx < suc->list.nr) {
+               ce  = suc->list.entries[idx];
+               strbuf_addf(err, _("Failed to clone '%s'. Retry scheduled"),
+                           ce->name);
+               strbuf_addch(err, '\n');
+               ALLOC_GROW(suc->failed_clones,
+                          suc->failed_clones_nr + 1,
+                          suc->failed_clones_alloc);
+               suc->failed_clones[suc->failed_clones_nr++] = ce;
+               return 0;
+       } else {
+               idx = suc->current - suc->list.nr;
+               ce  = suc->failed_clones[idx];
+               strbuf_addf(err, _("Failed to clone '%s' a second time, aborting"),
+                           ce->name);
+               strbuf_addch(err, '\n');
+               suc->quickstop = 1;
+               return 1;
+       }
+
+       return 0;
 }
 
 static int update_clone(int argc, const char **argv, const char *prefix)
@@ -778,6 +833,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
                              "specified number of revisions")),
                OPT_INTEGER('j', "jobs", &max_jobs,
                            N_("parallel jobs")),
+               OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
+                           N_("whether the initial clone should follow the shallow recommendation")),
                OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
                OPT_END()
        };
index b8b8522249337703eb66b245ad263e7dc1b41582..6cdfd5f7308df9df10afd90748a83d7abb61ca65 100644 (file)
@@ -759,7 +759,7 @@ static int do_reupdate(int ac, const char **av,
                if (save_nr != active_nr)
                        goto redo;
        }
-       free_pathspec(&pathspec);
+       clear_pathspec(&pathspec);
        return 0;
 }
 
index 96a2834a18be8ee03427cfdd408914ea71d59243..e866844685d519e2406c050da497c42506077f7d 100644 (file)
@@ -13,8 +13,8 @@
 
 static const char * const worktree_usage[] = {
        N_("git worktree add [<options>] <path> [<branch>]"),
-       N_("git worktree prune [<options>]"),
        N_("git worktree list [<options>]"),
+       N_("git worktree prune [<options>]"),
        NULL
 };
 
@@ -95,7 +95,7 @@ static void prune_worktrees(void)
        if (!dir)
                return;
        while ((d = readdir(dir)) != NULL) {
-               if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+               if (is_dot_or_dotdot(d->d_name))
                        continue;
                strbuf_reset(&reason);
                if (!prune_worktree(d->d_name, &reason))
@@ -262,7 +262,7 @@ static int add_worktree(const char *path, const char *refname,
         */
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
-       write_file(sb.buf, "0000000000000000000000000000000000000000");
+       write_file(sb.buf, sha1_to_hex(null_sha1));
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
        write_file(sb.buf, "../..");
@@ -337,9 +337,12 @@ static int add(int ac, const char **av, const char *prefix)
        if (ac < 1 || ac > 2)
                usage_with_options(worktree_usage, options);
 
-       path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
+       path = prefix_filename(prefix, strlen(prefix), av[0]);
        branch = ac < 2 ? "HEAD" : av[1];
 
+       if (!strcmp(branch, "-"))
+               branch = "@{-1}";
+
        opts.force_new_branch = !!new_branch_force;
        if (opts.force_new_branch) {
                struct strbuf symref = STRBUF_INIT;
@@ -467,6 +470,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 
        if (ac < 2)
                usage_with_options(worktree_usage, options);
+       if (!prefix)
+               prefix = "";
        if (!strcmp(av[1], "add"))
                return add(ac - 1, av + 1, prefix);
        if (!strcmp(av[1], "prune"))
diff --git a/cache.h b/cache.h
index c73becbf2dc8ae939116617e36c76afa60857cef..f1dc289d068f554b73434e9f8bf5d723a3e78519 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1604,6 +1604,16 @@ extern const char *get_log_output_encoding(void);
 extern const char *get_commit_output_encoding(void);
 
 extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
+
+enum config_scope {
+       CONFIG_SCOPE_UNKNOWN = 0,
+       CONFIG_SCOPE_SYSTEM,
+       CONFIG_SCOPE_GLOBAL,
+       CONFIG_SCOPE_REPO,
+       CONFIG_SCOPE_CMDLINE,
+};
+
+extern enum config_scope current_config_scope(void);
 extern const char *current_config_origin_type(void);
 extern const char *current_config_name(void);
 
@@ -1696,6 +1706,8 @@ extern int ignore_untracked_cache_config;
 struct key_value_info {
        const char *filename;
        int linenr;
+       const char *origin_type;
+       enum config_scope scope;
 };
 
 extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
@@ -1721,7 +1733,6 @@ extern int copy_file(const char *dst, const char *src, int mode);
 extern int copy_file_with_time(const char *dst, const char *src, int mode);
 
 extern void write_or_die(int fd, const void *buf, size_t count);
-extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
 extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
 extern void fsync_or_die(int fd, const char *);
 
diff --git a/color.c b/color.c
index 8f85153d0d2c1f4bf9340ced9371797eb3c92de9..81c26767239f9e028057c1843f57b77640db6c26 100644 (file)
--- a/color.c
+++ b/color.c
@@ -123,19 +123,34 @@ static int parse_color(struct color *out, const char *name, int len)
        return -1;
 }
 
-static int parse_attr(const char *name, int len)
+static int parse_attr(const char *name, size_t len)
 {
-       static const int attr_values[] = { 1, 2, 4, 5, 7,
-                                          22, 22, 24, 25, 27 };
-       static const char * const attr_names[] = {
-               "bold", "dim", "ul", "blink", "reverse",
-               "nobold", "nodim", "noul", "noblink", "noreverse"
+       static const struct {
+               const char *name;
+               size_t len;
+               int val, neg;
+       } attrs[] = {
+#define ATTR(x, val, neg) { (x), sizeof(x)-1, (val), (neg) }
+               ATTR("bold",      1, 22),
+               ATTR("dim",       2, 22),
+               ATTR("italic",    3, 23),
+               ATTR("ul",        4, 24),
+               ATTR("blink",     5, 25),
+               ATTR("reverse",   7, 27),
+               ATTR("strike",    9, 29)
+#undef ATTR
        };
+       int negate = 0;
        int i;
-       for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
-               const char *str = attr_names[i];
-               if (!strncasecmp(name, str, len) && !str[len])
-                       return attr_values[i];
+
+       if (skip_prefix_mem(name, len, "no", &name, &len)) {
+               skip_prefix_mem(name, len, "-", &name, &len);
+               negate = 1;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(attrs); i++) {
+               if (attrs[i].len == len && !memcmp(attrs[i].name, name, len))
+                       return negate ? attrs[i].neg : attrs[i].val;
        }
        return -1;
 }
diff --git a/color.h b/color.h
index e155d13f784362a5fdd993e1e383e660fa324c30..90627650fccc67c31261977a22544af131812bd1 100644 (file)
--- a/color.h
+++ b/color.h
@@ -3,26 +3,24 @@
 
 struct strbuf;
 
-/*  2 + (2 * num_attrs) + 8 + 1 + 8 + 'm' + NUL */
-/* "\033[1;2;4;5;7;38;5;2xx;48;5;2xxm\0" */
 /*
  * The maximum length of ANSI color sequence we would generate:
  * - leading ESC '['            2
- * - attr + ';'                 3 * 10 (e.g. "1;")
+ * - attr + ';'                 2 * num_attr (e.g. "1;")
+ * - no-attr + ';'              3 * num_attr (e.g. "22;")
  * - fg color + ';'             17 (e.g. "38;2;255;255;255;")
  * - bg color + ';'             17 (e.g. "48;2;255;255;255;")
  * - terminating 'm' NUL        2
  *
- * The above overcounts attr (we only use 5 not 8) and one semicolon
- * but it is close enough.
+ * The above overcounts by one semicolon but it is close enough.
+ *
+ * The space for attributes is also slightly overallocated, as
+ * the negation for some attributes is the same (e.g., nobold and nodim).
+ *
+ * We allocate space for 7 attributes.
  */
-#define COLOR_MAXLEN 70
+#define COLOR_MAXLEN 75
 
-/*
- * IMPORTANT: Due to the way these color codes are emulated on Windows,
- * write them only using printf(), fprintf(), and fputs(). In particular,
- * do not use puts() or write().
- */
 #define GIT_COLOR_NORMAL       ""
 #define GIT_COLOR_RESET                "\033[m"
 #define GIT_COLOR_BOLD         "\033[1m"
index 8f2313d5022326dc244fa8eba4a8640299ebeac8..5920df8b8dd12ff382131a6753f40bbdec64fd37 100644 (file)
@@ -1525,7 +1525,7 @@ void diff_tree_combined(const unsigned char *sha1,
                free(tmp);
        }
 
-       free_pathspec(&diffopts.pathspec);
+       clear_pathspec(&diffopts.pathspec);
 }
 
 void diff_tree_combined_merge(const struct commit *commit, int dense,
index 3f4f371e5eec41fa67345c0b9d373b541e52172a..24d4715f24f06c4747eb728ffbdab2aadf715e6a 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -414,7 +414,7 @@ int find_commit_subject(const char *commit_buffer, const char **subject)
        while (*p && (*p != '\n' || p[1] != '\n'))
                p++;
        if (*p) {
-               p += 2;
+               p = skip_blank_lines(p + 2);
                for (eol = p; *eol && *eol != '\n'; eol++)
                        ; /* do nothing */
        } else
index 0c923f05f73eacecf38602208c28211acd0b8f9e..3b88c8889db0f1b0e1f6ba2b6d504bb18fed13a2 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -131,11 +131,17 @@ enum cmit_fmt {
        CMIT_FMT_FULLER,
        CMIT_FMT_ONELINE,
        CMIT_FMT_EMAIL,
+       CMIT_FMT_MBOXRD,
        CMIT_FMT_USERFORMAT,
 
        CMIT_FMT_UNSPECIFIED
 };
 
+static inline int cmit_fmt_is_mail(enum cmit_fmt fmt)
+{
+       return (fmt == CMIT_FMT_EMAIL || fmt == CMIT_FMT_MBOXRD);
+}
+
 struct pretty_print_context {
        /*
         * Callers should tweak these to change the behavior of pp_* functions.
@@ -178,6 +184,7 @@ extern const char *format_subject(struct strbuf *sb, const char *msg,
                                  const char *line_separator);
 extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
 extern int commit_format_is_empty(enum cmit_fmt);
+extern const char *skip_blank_lines(const char *msg);
 extern void format_commit_message(const struct commit *commit,
                                  const char *format, struct strbuf *sb,
                                  const struct pretty_print_context *context);
index d7ce34b33d67c38245e77b7082d80f350e4f7eb7..bea937e4ecf8417a910882c1aafb263e792f2e1c 100644 (file)
--- a/config.c
+++ b/config.c
@@ -38,7 +38,33 @@ struct config_source {
        long (*do_ftell)(struct config_source *c);
 };
 
+/*
+ * These variables record the "current" config source, which
+ * can be accessed by parsing callbacks.
+ *
+ * The "cf" variable will be non-NULL only when we are actually parsing a real
+ * config source (file, blob, cmdline, etc).
+ *
+ * The "current_config_kvi" variable will be non-NULL only when we are feeding
+ * cached config from a configset into a callback.
+ *
+ * They should generally never be non-NULL at the same time. If they are both
+ * NULL, then we aren't parsing anything (and depending on the function looking
+ * at the variables, it's either a bug for it to be called in the first place,
+ * or it's a function which can be reused for non-config purposes, and should
+ * fall back to some sane behavior).
+ */
 static struct config_source *cf;
+static struct key_value_info *current_config_kvi;
+
+/*
+ * Similar to the variables above, this gives access to the "scope" of the
+ * current value (repo, global, etc). For cached values, it can be found via
+ * the current_config_kvi as above. During parsing, the current value can be
+ * found in this variable. It's not part of "cf" because it transcends a single
+ * file (i.e., a file included from .git/config is still in "repo" scope).
+ */
+static enum config_scope current_parsing_scope;
 
 static int zlib_compression_seen;
 
@@ -131,7 +157,9 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        if (!access_or_die(path, R_OK, 0)) {
                if (++inc->depth > MAX_INCLUDE_DEPTH)
                        die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
-                           cf && cf->name ? cf->name : "the command line");
+                           !cf ? "<unknown>" :
+                           cf->name ? cf->name :
+                           "the command line");
                ret = git_config_from_file(git_config_include, path, inc);
                inc->depth--;
        }
@@ -205,32 +233,40 @@ int git_config_parse_parameter(const char *text,
 int git_config_from_parameters(config_fn_t fn, void *data)
 {
        const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
+       int ret = 0;
        char *envw;
        const char **argv = NULL;
        int nr = 0, alloc = 0;
        int i;
+       struct config_source source;
 
        if (!env)
                return 0;
+
+       memset(&source, 0, sizeof(source));
+       source.prev = cf;
+       cf = &source;
+
        /* sq_dequote will write over it */
        envw = xstrdup(env);
 
        if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
-               free(envw);
-               return error("bogus format in " CONFIG_DATA_ENVIRONMENT);
+               ret = error("bogus format in " CONFIG_DATA_ENVIRONMENT);
+               goto out;
        }
 
        for (i = 0; i < nr; i++) {
                if (git_config_parse_parameter(argv[i], fn, data) < 0) {
-                       free(argv);
-                       free(envw);
-                       return -1;
+                       ret = -1;
+                       goto out;
                }
        }
 
+out:
        free(argv);
        free(envw);
-       return nr > 0;
+       cf = source.prev;
+       return ret;
 }
 
 static int get_next_char(void)
@@ -1197,47 +1233,36 @@ int git_config_system(void)
 
 static int do_git_config_sequence(config_fn_t fn, void *data)
 {
-       int ret = 0, found = 0;
+       int ret = 0;
        char *xdg_config = xdg_config_home("config");
        char *user_config = expand_user_path("~/.gitconfig");
        char *repo_config = git_pathdup("config");
 
-       if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
+       current_parsing_scope = CONFIG_SCOPE_SYSTEM;
+       if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
-               found += 1;
-       }
 
-       if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) {
+       current_parsing_scope = CONFIG_SCOPE_GLOBAL;
+       if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
                ret += git_config_from_file(fn, xdg_config, data);
-               found += 1;
-       }
 
-       if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) {
+       if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
                ret += git_config_from_file(fn, user_config, data);
-               found += 1;
-       }
 
-       if (repo_config && !access_or_die(repo_config, R_OK, 0)) {
+       current_parsing_scope = CONFIG_SCOPE_REPO;
+       if (repo_config && !access_or_die(repo_config, R_OK, 0))
                ret += git_config_from_file(fn, repo_config, data);
-               found += 1;
-       }
 
-       switch (git_config_from_parameters(fn, data)) {
-       case -1: /* error */
+       current_parsing_scope = CONFIG_SCOPE_CMDLINE;
+       if (git_config_from_parameters(fn, data) < 0)
                die(_("unable to parse command-line config"));
-               break;
-       case 0: /* found nothing */
-               break;
-       default: /* found at least one item */
-               found++;
-               break;
-       }
 
+       current_parsing_scope = CONFIG_SCOPE_UNKNOWN;
        free(xdg_config);
        free(user_config);
        free(repo_config);
-       return ret == 0 ? found : ret;
+       return ret;
 }
 
 int git_config_with_options(config_fn_t fn, void *data,
@@ -1272,7 +1297,7 @@ static void git_config_raw(config_fn_t fn, void *data)
        if (git_config_with_options(fn, data, NULL, 1) < 0)
                /*
                 * git_config_with_options() normally returns only
-                * positive values, as most errors are fatal, and
+                * zero, as most errors are fatal, and
                 * non-fatal potential errors are guarded by "if"
                 * statements that are entered only when no error is
                 * possible.
@@ -1290,16 +1315,20 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
        struct string_list *values;
        struct config_set_element *entry;
        struct configset_list *list = &cs->list;
-       struct key_value_info *kv_info;
 
        for (i = 0; i < list->nr; i++) {
                entry = list->items[i].e;
                value_index = list->items[i].value_index;
                values = &entry->value_list;
-               if (fn(entry->key, values->items[value_index].string, data) < 0) {
-                       kv_info = values->items[value_index].util;
-                       git_die_config_linenr(entry->key, kv_info->filename, kv_info->linenr);
-               }
+
+               current_config_kvi = values->items[value_index].util;
+
+               if (fn(entry->key, values->items[value_index].string, data) < 0)
+                       git_die_config_linenr(entry->key,
+                                             current_config_kvi->filename,
+                                             current_config_kvi->linenr);
+
+               current_config_kvi = NULL;
        }
 }
 
@@ -1356,14 +1385,19 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
        l_item->e = e;
        l_item->value_index = e->value_list.nr - 1;
 
-       if (cf) {
+       if (!cf)
+               die("BUG: configset_add_value has no source");
+       if (cf->name) {
                kv_info->filename = strintern(cf->name);
                kv_info->linenr = cf->linenr;
+               kv_info->origin_type = strintern(cf->origin_type);
        } else {
                /* for values read from `git_config_from_parameters()` */
                kv_info->filename = NULL;
                kv_info->linenr = -1;
+               kv_info->origin_type = NULL;
        }
+       kv_info->scope = current_parsing_scope;
        si->util = kv_info;
 
        return 0;
@@ -2442,10 +2476,32 @@ int parse_config_key(const char *var,
 
 const char *current_config_origin_type(void)
 {
-       return cf && cf->origin_type ? cf->origin_type : "command line";
+       const char *type;
+       if (current_config_kvi)
+               type = current_config_kvi->origin_type;
+       else if(cf)
+               type = cf->origin_type;
+       else
+               die("BUG: current_config_origin_type called outside config callback");
+       return type ? type : "command line";
 }
 
 const char *current_config_name(void)
 {
-       return cf && cf->name ? cf->name : "";
+       const char *name;
+       if (current_config_kvi)
+               name = current_config_kvi->filename;
+       else if (cf)
+               name = cf->name;
+       else
+               die("BUG: current_config_name called outside config callback");
+       return name ? name : "";
+}
+
+enum config_scope current_config_scope(void)
+{
+       if (current_config_kvi)
+               return current_config_kvi->scope;
+       else
+               return current_parsing_scope;
 }
index 34024754d9296b56c194a7959afb7b7f28402457..37888f4e570e7f268bdc3e735c63f7a136774aa3 100644 (file)
@@ -803,6 +803,50 @@ __git_find_on_cmdline ()
        done
 }
 
+# Echo the value of an option set on the command line or config
+#
+# $1: short option name
+# $2: long option name including =
+# $3: list of possible values
+# $4: config string (optional)
+#
+# example:
+# result="$(__git_get_option_value "-d" "--do-something=" \
+#     "yes no" "core.doSomething")"
+#
+# result is then either empty (no option set) or "yes" or "no"
+#
+# __git_get_option_value requires 3 arguments
+__git_get_option_value ()
+{
+       local c short_opt long_opt val
+       local result= values config_key word
+
+       short_opt="$1"
+       long_opt="$2"
+       values="$3"
+       config_key="$4"
+
+       ((c = $cword - 1))
+       while [ $c -ge 0 ]; do
+               word="${words[c]}"
+               for val in $values; do
+                       if [ "$short_opt$val" = "$word" ] ||
+                          [ "$long_opt$val"  = "$word" ]; then
+                               result="$val"
+                               break 2
+                       fi
+               done
+               ((c--))
+       done
+
+       if [ -n "$config_key" ] && [ -z "$result" ]; then
+               result="$(git --git-dir="$(__gitdir)" config "$config_key")"
+       fi
+
+       echo "$result"
+}
+
 __git_has_doubledash ()
 {
        local c=1
@@ -1098,6 +1142,8 @@ _git_clone ()
        esac
 }
 
+__git_untracked_file_modes="all no normal"
+
 _git_commit ()
 {
        case "$prev" in
@@ -1119,7 +1165,7 @@ _git_commit ()
                return
                ;;
        --untracked-files=*)
-               __gitcomp "all no normal" "" "${cur##--untracked-files=}"
+               __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}"
                return
                ;;
        --*)
@@ -1780,6 +1826,56 @@ _git_stage ()
        _git_add
 }
 
+_git_status ()
+{
+       local complete_opt
+       local untracked_state
+
+       case "$cur" in
+       --ignore-submodules=*)
+               __gitcomp "none untracked dirty all" "" "${cur##--ignore-submodules=}"
+               return
+               ;;
+       --untracked-files=*)
+               __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}"
+               return
+               ;;
+       --column=*)
+               __gitcomp "
+                       always never auto column row plain dense nodense
+                       " "" "${cur##--column=}"
+               return
+               ;;
+       --*)
+               __gitcomp "
+                       --short --branch --porcelain --long --verbose
+                       --untracked-files= --ignore-submodules= --ignored
+                       --column= --no-column
+                       "
+               return
+               ;;
+       esac
+
+       untracked_state="$(__git_get_option_value "-u" "--untracked-files=" \
+               "$__git_untracked_file_modes" "status.showUntrackedFiles")"
+
+       case "$untracked_state" in
+       no)
+               # --ignored option does not matter
+               complete_opt=
+               ;;
+       all|normal|*)
+               complete_opt="--cached --directory --no-empty-directory --others"
+
+               if [ -n "$(__git_find_on_cmdline "--ignored")" ]; then
+                       complete_opt="$complete_opt --ignored --exclude=*"
+               fi
+               ;;
+       esac
+
+       __git_complete_index_file "$complete_opt"
+}
+
 __git_config_get_set_variables ()
 {
        local prevword word config_file= c=$cword
@@ -2595,6 +2691,29 @@ _git_whatchanged ()
        _git_log
 }
 
+_git_worktree ()
+{
+       local subcommands="add list prune"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
+       if [ -z "$subcommand" ]; then
+               __gitcomp "$subcommands"
+       else
+               case "$subcommand,$cur" in
+               add,--*)
+                       __gitcomp "--detach"
+                       ;;
+               list,--*)
+                       __gitcomp "--porcelain"
+                       ;;
+               prune,--*)
+                       __gitcomp "--dry-run --expire --verbose"
+                       ;;
+               *)
+                       ;;
+               esac
+       fi
+}
+
 __git_main ()
 {
        local i c=1 command __git_dir
index c504ef752db124e21156be5b92360dbe432e568f..59630cee1488bda274bd4f4bd8bf2748d9ab081a 100644 (file)
@@ -166,6 +166,7 @@ Format of STDIN stream:
 #include "quote.h"
 #include "exec_cmd.h"
 #include "dir.h"
+#include "run-command.h"
 
 #define PACK_ID_BITS 16
 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@ -282,6 +283,7 @@ struct recent_command {
 /* Configured limits on output */
 static unsigned long max_depth = 10;
 static off_t max_packsize;
+static int unpack_limit = 100;
 static int force_update;
 static int pack_compression_level = Z_DEFAULT_COMPRESSION;
 static int pack_compression_seen;
@@ -596,6 +598,33 @@ static struct object_entry *insert_object(unsigned char *sha1)
        return e;
 }
 
+static void invalidate_pack_id(unsigned int id)
+{
+       unsigned int h;
+       unsigned long lu;
+       struct tag *t;
+
+       for (h = 0; h < ARRAY_SIZE(object_table); h++) {
+               struct object_entry *e;
+
+               for (e = object_table[h]; e; e = e->next)
+                       if (e->pack_id == id)
+                               e->pack_id = MAX_PACK_ID;
+       }
+
+       for (lu = 0; lu < branch_table_sz; lu++) {
+               struct branch *b;
+
+               for (b = branch_table[lu]; b; b = b->table_next_branch)
+                       if (b->pack_id == id)
+                               b->pack_id = MAX_PACK_ID;
+       }
+
+       for (t = first_tag; t; t = t->next_tag)
+               if (t->pack_id == id)
+                       t->pack_id = MAX_PACK_ID;
+}
+
 static unsigned int hc_str(const char *s, size_t len)
 {
        unsigned int r = 0;
@@ -951,6 +980,23 @@ static void unkeep_all_packs(void)
        }
 }
 
+static int loosen_small_pack(const struct packed_git *p)
+{
+       struct child_process unpack = CHILD_PROCESS_INIT;
+
+       if (lseek(p->pack_fd, 0, SEEK_SET) < 0)
+               die_errno("Failed seeking to start of '%s'", p->pack_name);
+
+       unpack.in = p->pack_fd;
+       unpack.git_cmd = 1;
+       unpack.stdout_to_stderr = 1;
+       argv_array_push(&unpack.args, "unpack-objects");
+       if (!show_stats)
+               argv_array_push(&unpack.args, "-q");
+
+       return run_command(&unpack);
+}
+
 static void end_packfile(void)
 {
        static int running;
@@ -973,6 +1019,14 @@ static void end_packfile(void)
                fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
                                    pack_data->pack_name, object_count,
                                    cur_pack_sha1, pack_size);
+
+               if (object_count <= unpack_limit) {
+                       if (!loosen_small_pack(pack_data)) {
+                               invalidate_pack_id(pack_id);
+                               goto discard_pack;
+                       }
+               }
+
                close(pack_data->pack_fd);
                idx_name = keep_pack(create_index());
 
@@ -1003,6 +1057,7 @@ static void end_packfile(void)
                pack_id++;
        }
        else {
+discard_pack:
                close(pack_data->pack_fd);
                unlink_or_warn(pack_data->pack_name);
        }
@@ -3320,6 +3375,7 @@ static void parse_option(const char *option)
 static void git_pack_config(void)
 {
        int indexversion_value;
+       int limit;
        unsigned long packsizelimit_value;
 
        if (!git_config_get_ulong("pack.depth", &max_depth)) {
@@ -3344,6 +3400,11 @@ static void git_pack_config(void)
        if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value))
                max_packsize = packsizelimit_value;
 
+       if (!git_config_get_int("fastimport.unpacklimit", &limit))
+               unpack_limit = limit;
+       else if (!git_config_get_int("transfer.unpacklimit", &limit))
+               unpack_limit = limit;
+
        git_config(git_default_config, NULL);
 }
 
index 49d4029b8dddcb06dc6bea3d5f47c020785e3ddf..c99cddc54b1ea14955e019dc84088b6c9fd74f88 100644 (file)
@@ -473,6 +473,23 @@ static inline int skip_prefix(const char *str, const char *prefix,
        return 0;
 }
 
+/*
+ * Like skip_prefix, but promises never to read past "len" bytes of the input
+ * buffer, and returns the remaining number of bytes in "out" via "outlen".
+ */
+static inline int skip_prefix_mem(const char *buf, size_t len,
+                                 const char *prefix,
+                                 const char **out, size_t *outlen)
+{
+       size_t prefix_len = strlen(prefix);
+       if (prefix_len <= len && !memcmp(buf, prefix, prefix_len)) {
+               *out = buf + prefix_len;
+               *outlen = len - prefix_len;
+               return 1;
+       }
+       return 0;
+}
+
 /*
  * If buf ends with suffix, return 1 and subtract the length of the suffix
  * from *len. Otherwise, return 0 and leave *len untouched.
index 69587856df1706a2da42d6ce2a87c25a054e9eb6..da81be40cb7f9af1a960be2c22f8e75c9d16822e 100755 (executable)
@@ -621,6 +621,8 @@ sub is_format_patch_arg {
        push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
 }
 
+@files = handle_backup_files(@files);
+
 if ($validate) {
        foreach my $f (@files) {
                unless (-p $f) {
@@ -1727,6 +1729,44 @@ sub validate_patch {
        return;
 }
 
+sub handle_backup {
+       my ($last, $lastlen, $file, $known_suffix) = @_;
+       my ($suffix, $skip);
+
+       $skip = 0;
+       if (defined $last &&
+           ($lastlen < length($file)) &&
+           (substr($file, 0, $lastlen) eq $last) &&
+           ($suffix = substr($file, $lastlen)) !~ /^[a-z0-9]/i) {
+               if (defined $known_suffix && $suffix eq $known_suffix) {
+                       print "Skipping $file with backup suffix '$known_suffix'.\n";
+                       $skip = 1;
+               } else {
+                       my $answer = ask("Do you really want to send $file? (y|N): ",
+                                        valid_re => qr/^(?:y|n)/i,
+                                        default => 'n');
+                       $skip = ($answer ne 'y');
+                       if ($skip) {
+                               $known_suffix = $suffix;
+                       }
+               }
+       }
+       return ($skip, $known_suffix);
+}
+
+sub handle_backup_files {
+       my @file = @_;
+       my ($last, $lastlen, $known_suffix, $skip, @result);
+       for my $file (@file) {
+               ($skip, $known_suffix) = handle_backup($last, $lastlen,
+                                                      $file, $known_suffix);
+               push @result, $file unless $skip;
+               $last = $file;
+               $lastlen = length($file);
+       }
+       return @result;
+}
+
 sub file_has_nonascii {
        my $fn = shift;
        open(my $fh, '<', $fn)
index 7fe8a511b37a9705249132afedb57032fb65e86c..7c62b53cc0dfcf47770a8f30a59f625c8657c948 100755 (executable)
@@ -9,7 +9,7 @@ USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <re
    or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
    or: $dashless [--quiet] init [--] [<path>...]
    or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...)
-   or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--reference <repository>] [--recursive] [--] [<path>...]
+   or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--] [<path>...]
    or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
    or: $dashless [--quiet] foreach [--recursive] <command>
    or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
@@ -530,6 +530,12 @@ cmd_update()
                --checkout)
                        update="checkout"
                        ;;
+               --recommend-shallow)
+                       recommend_shallow="--recommend-shallow"
+                       ;;
+               --no-recommend-shallow)
+                       recommend_shallow="--no-recommend-shallow"
+                       ;;
                --depth)
                        case "$2" in '') usage ;; esac
                        depth="--depth=$2"
@@ -572,6 +578,7 @@ cmd_update()
                ${update:+--update "$update"} \
                ${reference:+--reference "$reference"} \
                ${depth:+--depth "$depth"} \
+               ${recommend_shallow:+"$recommend_shallow"} \
                ${jobs:+$jobs} \
                "$@" || echo "#unmatched"
        } | {
@@ -611,7 +618,7 @@ cmd_update()
                        if test -z "$nofetch"
                        then
                                # Fetch remote before determining tracking $sha1
-                               (sanitize_submodule_env; cd "$sm_path" && git-fetch) ||
+                               fetch_in_submodule "$sm_path" ||
                                die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
                        fi
                        remote_name=$(sanitize_submodule_env; cd "$sm_path" && get_default_remote)
@@ -698,7 +705,7 @@ cmd_update()
                        if test $res -gt 0
                        then
                                die_msg="$(eval_gettext "Failed to recurse into submodule path '\$displaypath'")"
-                               if test $res -eq 1
+                               if test $res -ne 2
                                then
                                        err="${err};$die_msg"
                                        continue
index 05eced06cde8a1ff83262d452dc8edbcc1f3dea8..f609e54ce3b3d5a0f004de6d40390a9fdae4f6a9 100755 (executable)
@@ -507,7 +507,10 @@ sub init_subdir {
 
 sub cmd_clone {
        my ($url, $path) = @_;
-       if (!defined $path &&
+       if (!$url) {
+               die "SVN repository location required ",
+                   "as a command-line argument\n";
+       } elsif (!defined $path &&
            (defined $_trunk || @_branches || @_tags ||
             defined $_stdlayout) &&
            $url !~ m#^[a-z\+]+://#) {
index c4b1e8c78d396194753d8a7287678bc476f858d6..08356f92e7b39787deb145868b01bf4c2cf0102a 100644 (file)
@@ -3,6 +3,7 @@
 #include "strbuf.h"
 #include "gpg-interface.h"
 #include "sigchain.h"
+#include "tempfile.h"
 
 static char *configured_signing_key;
 static const char *gpg_program = "gpg";
@@ -150,42 +151,30 @@ const char *get_signing_key(void)
 int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
 {
        struct child_process gpg = CHILD_PROCESS_INIT;
-       const char *args[4];
-       ssize_t len;
+       int ret;
        size_t i, j, bottom;
+       struct strbuf gpg_status = STRBUF_INIT;
 
-       gpg.argv = args;
-       gpg.in = -1;
-       gpg.out = -1;
-       args[0] = gpg_program;
-       args[1] = "-bsau";
-       args[2] = signing_key;
-       args[3] = NULL;
+       argv_array_pushl(&gpg.args,
+                        gpg_program,
+                        "--status-fd=2",
+                        "-bsau", signing_key,
+                        NULL);
 
-       if (start_command(&gpg))
-               return error(_("could not run gpg."));
+       bottom = signature->len;
 
        /*
         * When the username signingkey is bad, program could be terminated
         * because gpg exits without reading and then write gets SIGPIPE.
         */
        sigchain_push(SIGPIPE, SIG_IGN);
-
-       if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
-               close(gpg.in);
-               close(gpg.out);
-               finish_command(&gpg);
-               return error(_("gpg did not accept the data"));
-       }
-       close(gpg.in);
-
-       bottom = signature->len;
-       len = strbuf_read(signature, gpg.out, 1024);
-       close(gpg.out);
-
+       ret = pipe_command(&gpg, buffer->buf, buffer->len,
+                          signature, 1024, &gpg_status, 0);
        sigchain_pop(SIGPIPE);
 
-       if (finish_command(&gpg) || !len || len < 0)
+       ret |= !strstr(gpg_status.buf, "\n[GNUPG:] SIG_CREATED ");
+       strbuf_release(&gpg_status);
+       if (ret)
                return error(_("gpg failed to sign the data"));
 
        /* Strip CR from the line endings, in case we are on Windows. */
@@ -210,50 +199,38 @@ 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;
-       const char *args_gpg[] = {NULL, "--status-fd=1", "--verify", "FILE", "-", NULL};
-       char path[PATH_MAX];
+       static struct tempfile temp;
        int fd, ret;
        struct strbuf buf = STRBUF_INIT;
-       struct strbuf *pbuf = &buf;
 
-       args_gpg[0] = gpg_program;
-       fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
+       fd = mks_tempfile_t(&temp, ".git_vtag_tmpXXXXXX");
        if (fd < 0)
-               return error_errno(_("could not create temporary file '%s'"), path);
-       if (write_in_full(fd, signature, signature_size) < 0)
-               return error_errno(_("failed writing detached signature to '%s'"), path);
+               return error_errno(_("could not create temporary file"));
+       if (write_in_full(fd, signature, signature_size) < 0) {
+               error_errno(_("failed writing detached signature to '%s'"),
+                           temp.filename.buf);
+               delete_tempfile(&temp);
+               return -1;
+       }
        close(fd);
 
-       gpg.argv = args_gpg;
-       gpg.in = -1;
-       gpg.out = -1;
-       if (gpg_output)
-               gpg.err = -1;
-       args_gpg[3] = path;
-       if (start_command(&gpg)) {
-               unlink(path);
-               return error(_("could not run gpg."));
-       }
+       argv_array_pushl(&gpg.args,
+                        gpg_program,
+                        "--status-fd=1",
+                        "--verify", temp.filename.buf, "-",
+                        NULL);
 
-       sigchain_push(SIGPIPE, SIG_IGN);
-       write_in_full(gpg.in, payload, payload_size);
-       close(gpg.in);
+       if (!gpg_status)
+               gpg_status = &buf;
 
-       if (gpg_output) {
-               strbuf_read(gpg_output, gpg.err, 0);
-               close(gpg.err);
-       }
-       if (gpg_status)
-               pbuf = gpg_status;
-       strbuf_read(pbuf, gpg.out, 0);
-       close(gpg.out);
-
-       ret = finish_command(&gpg);
+       sigchain_push(SIGPIPE, SIG_IGN);
+       ret = pipe_command(&gpg, payload, payload_size,
+                          gpg_status, 0, gpg_output, 0);
        sigchain_pop(SIGPIPE);
 
-       unlink_or_warn(path);
+       delete_tempfile(&temp);
 
-       ret |= !strstr(pbuf->buf, "\n[GNUPG:] GOODSIG ");
+       ret |= !strstr(gpg_status->buf, "\n[GNUPG:] GOODSIG ");
        strbuf_release(&buf); /* no matter it was used or not */
 
        return ret;
diff --git a/http.c b/http.c
index df6dd01594a0c7bacbb9beacec683072de38a5b1..d8b2bec861b14dfa16dbe99f3ccc44463375b8b3 100644 (file)
--- a/http.c
+++ b/http.c
@@ -11,6 +11,7 @@
 #include "gettext.h"
 #include "transport.h"
 
+static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
 #if LIBCURL_VERSION_NUM >= 0x070a08
 long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
 #else
@@ -477,6 +478,125 @@ static void set_curl_keepalive(CURL *c)
 }
 #endif
 
+static void redact_sensitive_header(struct strbuf *header)
+{
+       const char *sensitive_header;
+
+       if (skip_prefix(header->buf, "Authorization:", &sensitive_header) ||
+           skip_prefix(header->buf, "Proxy-Authorization:", &sensitive_header)) {
+               /* The first token is the type, which is OK to log */
+               while (isspace(*sensitive_header))
+                       sensitive_header++;
+               while (*sensitive_header && !isspace(*sensitive_header))
+                       sensitive_header++;
+               /* Everything else is opaque and possibly sensitive */
+               strbuf_setlen(header,  sensitive_header - header->buf);
+               strbuf_addstr(header, " <redacted>");
+       }
+}
+
+static void curl_dump_header(const char *text, unsigned char *ptr, size_t size, int hide_sensitive_header)
+{
+       struct strbuf out = STRBUF_INIT;
+       struct strbuf **headers, **header;
+
+       strbuf_addf(&out, "%s, %10.10ld bytes (0x%8.8lx)\n",
+               text, (long)size, (long)size);
+       trace_strbuf(&trace_curl, &out);
+       strbuf_reset(&out);
+       strbuf_add(&out, ptr, size);
+       headers = strbuf_split_max(&out, '\n', 0);
+
+       for (header = headers; *header; header++) {
+               if (hide_sensitive_header)
+                       redact_sensitive_header(*header);
+               strbuf_insert((*header), 0, text, strlen(text));
+               strbuf_insert((*header), strlen(text), ": ", 2);
+               strbuf_rtrim((*header));
+               strbuf_addch((*header), '\n');
+               trace_strbuf(&trace_curl, (*header));
+       }
+       strbuf_list_free(headers);
+       strbuf_release(&out);
+}
+
+static void curl_dump_data(const char *text, unsigned char *ptr, size_t size)
+{
+       size_t i;
+       struct strbuf out = STRBUF_INIT;
+       unsigned int width = 60;
+
+       strbuf_addf(&out, "%s, %10.10ld bytes (0x%8.8lx)\n",
+               text, (long)size, (long)size);
+       trace_strbuf(&trace_curl, &out);
+
+       for (i = 0; i < size; i += width) {
+               size_t w;
+
+               strbuf_reset(&out);
+               strbuf_addf(&out, "%s: ", text);
+               for (w = 0; (w < width) && (i + w < size); w++) {
+                       unsigned char ch = ptr[i + w];
+
+                       strbuf_addch(&out,
+                                      (ch >= 0x20) && (ch < 0x80)
+                                      ? ch : '.');
+               }
+               strbuf_addch(&out, '\n');
+               trace_strbuf(&trace_curl, &out);
+       }
+       strbuf_release(&out);
+}
+
+static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp)
+{
+       const char *text;
+       enum { NO_FILTER = 0, DO_FILTER = 1 };
+
+       switch (type) {
+       case CURLINFO_TEXT:
+               trace_printf_key(&trace_curl, "== Info: %s", data);
+       default:                /* we ignore unknown types by default */
+               return 0;
+
+       case CURLINFO_HEADER_OUT:
+               text = "=> Send header";
+               curl_dump_header(text, (unsigned char *)data, size, DO_FILTER);
+               break;
+       case CURLINFO_DATA_OUT:
+               text = "=> Send data";
+               curl_dump_data(text, (unsigned char *)data, size);
+               break;
+       case CURLINFO_SSL_DATA_OUT:
+               text = "=> Send SSL data";
+               curl_dump_data(text, (unsigned char *)data, size);
+               break;
+       case CURLINFO_HEADER_IN:
+               text = "<= Recv header";
+               curl_dump_header(text, (unsigned char *)data, size, NO_FILTER);
+               break;
+       case CURLINFO_DATA_IN:
+               text = "<= Recv data";
+               curl_dump_data(text, (unsigned char *)data, size);
+               break;
+       case CURLINFO_SSL_DATA_IN:
+               text = "<= Recv SSL data";
+               curl_dump_data(text, (unsigned char *)data, size);
+               break;
+       }
+       return 0;
+}
+
+void setup_curl_trace(CURL *handle)
+{
+       if (!trace_want(&trace_curl))
+               return;
+       curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
+       curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, curl_trace);
+       curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL);
+}
+
+
 static CURL *get_curl_handle(void)
 {
        CURL *result = curl_easy_init();
@@ -575,9 +695,9 @@ static CURL *get_curl_handle(void)
                warning("protocol restrictions not applied to curl redirects because\n"
                        "your curl version is too old (>= 7.19.4)");
 #endif
-
        if (getenv("GIT_CURL_VERBOSE"))
-               curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
+               curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
+       setup_curl_trace(result);
 
        curl_easy_setopt(result, CURLOPT_USERAGENT,
                user_agent ? user_agent : git_user_agent());
diff --git a/http.h b/http.h
index 36f558bfb379a990ec4d4aa0d871edd2a6d250a1..5ab9d9c329378f4f254c9d912dd7f05105ea6695 100644 (file)
--- a/http.h
+++ b/http.h
@@ -225,4 +225,6 @@ extern int finish_http_object_request(struct http_object_request *freq);
 extern void abort_http_object_request(struct http_object_request *freq);
 extern void release_http_object_request(struct http_object_request *freq);
 
+/* setup routine for curl_easy_setopt CURLOPT_DEBUGFUNCTION */
+void setup_curl_trace(CURL *handle);
 #endif /* HTTP_H */
index 938c6915858b93b7c860e49e906c45e0e2ea5d03..50377c5b88d59a3563b49e669b93f17009bcdd19 100644 (file)
@@ -1443,6 +1443,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc)
 
        if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+       setup_curl_trace(curl);
 
        return curl;
 }
index 8d393150c094f55638e4035e9c6121973d3ce9db..9f678abc92a82ce8c0feac6b66044291b730aac9 100644 (file)
@@ -603,7 +603,7 @@ void show_log(struct rev_info *opt)
         * Print header line of header..
         */
 
-       if (opt->commit_format == CMIT_FMT_EMAIL) {
+       if (cmit_fmt_is_mail(opt->commit_format)) {
                log_write_email_headers(opt, commit, &ctx.subject, &extra_headers,
                                        &ctx.need_8bit_cte);
        } else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
@@ -696,7 +696,7 @@ void show_log(struct rev_info *opt)
 
        if ((ctx.fmt != CMIT_FMT_USERFORMAT) &&
            ctx.notes_message && *ctx.notes_message) {
-               if (ctx.fmt == CMIT_FMT_EMAIL) {
+               if (cmit_fmt_is_mail(ctx.fmt)) {
                        strbuf_addstr(&msgbuf, "---\n");
                        opt->shown_dashes = 1;
                }
index 34bfac0c685f18c635b30dbf19d7b25250faa1c6..b7814c9b6488825a68bf8ca20de2c6420f01e26b 100644 (file)
@@ -170,7 +170,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
                       sha1_to_hex(mp->remote));
        }
        diff_flush(&opt);
-       free_pathspec(&opt.pathspec);
+       clear_pathspec(&opt.pathspec);
 
        *num_changes = len;
        return changes;
@@ -256,7 +256,7 @@ static void diff_tree_local(struct notes_merge_options *o,
                       sha1_to_hex(mp->local));
        }
        diff_flush(&opt);
-       free_pathspec(&opt.pathspec);
+       clear_pathspec(&opt.pathspec);
 }
 
 static void check_notes_merge_worktree(struct notes_merge_options *o)
diff --git a/notes.c b/notes.c
index e4e4854d69fb965e68a712dd342121d3276be945..df4660fe62ae64356950ca51c1acead0398f93f1 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -70,7 +70,7 @@ struct non_note {
 
 struct notes_tree default_notes_tree;
 
-static struct string_list display_notes_refs;
+static struct string_list display_notes_refs = STRING_LIST_INIT_NODUP;
 static struct notes_tree **display_notes_trees;
 
 static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
index 239898d946f06d102030569fd45780f523fdcd5a..ba5acf3111d809a3c77314fb2c0852854620cfae 100644 (file)
@@ -144,7 +144,7 @@ int parse_opt_string_list(const struct option *opt, const char *arg, int unset)
        if (!arg)
                return -1;
 
-       string_list_append(v, xstrdup(arg));
+       string_list_append(v, arg);
        return 0;
 }
 
index c9e9b6c0778c755fcade1430cf674dfd867050f1..24e0dd52322baf4a507765ae0948c98f0ec30602 100644 (file)
@@ -489,7 +489,7 @@ void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
               sizeof(struct pathspec_item) * dst->nr);
 }
 
-void free_pathspec(struct pathspec *pathspec)
+void clear_pathspec(struct pathspec *pathspec)
 {
        free(pathspec->items);
        pathspec->items = NULL;
index 0c1126264a394e62cfbed47b2feae85e9b5d1410..4a80f6fc96a572681acd8aa8349b2c0b1237f9f1 100644 (file)
@@ -19,7 +19,7 @@
 #define PATHSPEC_ONESTAR 1     /* the pathspec pattern satisfies GFNM_ONESTAR */
 
 struct pathspec {
-       const char **_raw; /* get_pathspec() result, not freed by free_pathspec() */
+       const char **_raw; /* get_pathspec() result, not freed by clear_pathspec() */
        int nr;
        unsigned int has_wildcard:1;
        unsigned int recursive:1;
@@ -74,7 +74,7 @@ extern void parse_pathspec(struct pathspec *pathspec,
                           const char *prefix,
                           const char **args);
 extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
-extern void free_pathspec(struct pathspec *);
+extern void clear_pathspec(struct pathspec *);
 
 static inline int ps_strncmp(const struct pathspec_item *item,
                             const char *s1, const char *s2, size_t n)
index d94d01cfdc8e4bcae2673b2421ef6e48fd69fb24..018beb85a09d6ef524c326c3ec947982c05bf556 100644 (file)
@@ -97,8 +97,12 @@ sub resolve_local_globs {
                                    "existing: $existing\n",
                                    " globbed: $refname\n";
                        }
-                       my $u = (::cmt_metadata("$refname"))[0] or die
-                           "$refname: no associated commit metadata\n";
+                       my $u = (::cmt_metadata("$refname"))[0];
+                       if (!defined($u)) {
+                               warn
+"W: $refname: no associated commit metadata from SVN, skipping\n";
+                               next;
+                       }
                        $u =~ s!^\Q$url\E(/|$)!! or die
                          "$refname: '$url' not found in '$u'\n";
                        if ($pathname ne $u) {
@@ -1905,15 +1909,22 @@ sub make_log_entry {
 
        my @parents = @$parents;
        my $props = $ed->{dir_prop}{$self->path};
-       if ( $props->{"svk:merge"} ) {
-               $self->find_extra_svk_parents($props->{"svk:merge"}, \@parents);
-       }
-       if ( $props->{"svn:mergeinfo"} ) {
-               my $mi_changes = $self->mergeinfo_changes
-                       ($parent_path, $parent_rev,
-                        $self->path, $rev,
-                        $props->{"svn:mergeinfo"});
-               $self->find_extra_svn_parents($mi_changes, \@parents);
+       if ($self->follow_parent) {
+               my $tickets = $props->{"svk:merge"};
+               if ($tickets) {
+                       $self->find_extra_svk_parents($tickets, \@parents);
+               }
+
+               my $mergeinfo_prop = $props->{"svn:mergeinfo"};
+               if ($mergeinfo_prop) {
+                       my $mi_changes = $self->mergeinfo_changes(
+                                               $parent_path,
+                                               $parent_rev,
+                                               $self->path,
+                                               $rev,
+                                               $mergeinfo_prop);
+                       $self->find_extra_svn_parents($mi_changes, \@parents);
+               }
        }
 
        open my $un, '>>', "$self->{dir}/unhandled.log" or croak $!;
index 8d182472e7d0011bb2edbb437cc122a4889cbbed..9fa42c2b4e3cd55fe8ec03248bc19780574bf961 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -92,6 +92,7 @@ static void setup_commit_formats(void)
                { "medium",     CMIT_FMT_MEDIUM,        0,      8 },
                { "short",      CMIT_FMT_SHORT,         0,      0 },
                { "email",      CMIT_FMT_EMAIL,         0,      0 },
+               { "mboxrd",     CMIT_FMT_MBOXRD,        0,      0 },
                { "fuller",     CMIT_FMT_FULLER,        0,      8 },
                { "full",       CMIT_FMT_FULL,          0,      8 },
                { "oneline",    CMIT_FMT_ONELINE,       1,      0 }
@@ -444,7 +445,7 @@ void pp_user_info(struct pretty_print_context *pp,
        if (pp->mailmap)
                map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
 
-       if (pp->fmt == CMIT_FMT_EMAIL) {
+       if (cmit_fmt_is_mail(pp->fmt)) {
                if (pp->from_ident && ident_cmp(pp->from_ident, &ident)) {
                        struct strbuf buf = STRBUF_INIT;
 
@@ -494,6 +495,7 @@ void pp_user_info(struct pretty_print_context *pp,
                            show_ident_date(&ident, &pp->date_mode));
                break;
        case CMIT_FMT_EMAIL:
+       case CMIT_FMT_MBOXRD:
                strbuf_addf(sb, "Date: %s\n",
                            show_ident_date(&ident, DATE_MODE(RFC2822)));
                break;
@@ -507,7 +509,7 @@ void pp_user_info(struct pretty_print_context *pp,
        }
 }
 
-static int is_empty_line(const char *line, int *len_p)
+static int is_blank_line(const char *line, int *len_p)
 {
        int len = *len_p;
        while (len && isspace(line[len - 1]))
@@ -516,14 +518,14 @@ static int is_empty_line(const char *line, int *len_p)
        return !len;
 }
 
-static const char *skip_empty_lines(const char *msg)
+const char *skip_blank_lines(const char *msg)
 {
        for (;;) {
                int linelen = get_one_line(msg);
                int ll = linelen;
                if (!linelen)
                        break;
-               if (!is_empty_line(msg, &ll))
+               if (!is_blank_line(msg, &ll))
                        break;
                msg += linelen;
        }
@@ -535,7 +537,7 @@ static void add_merge_info(const struct pretty_print_context *pp,
 {
        struct commit_list *parent = commit->parents;
 
-       if ((pp->fmt == CMIT_FMT_ONELINE) || (pp->fmt == CMIT_FMT_EMAIL) ||
+       if ((pp->fmt == CMIT_FMT_ONELINE) || (cmit_fmt_is_mail(pp->fmt)) ||
            !parent || !parent->next)
                return;
 
@@ -875,7 +877,7 @@ const char *format_subject(struct strbuf *sb, const char *msg,
                int linelen = get_one_line(line);
 
                msg += linelen;
-               if (!linelen || is_empty_line(line, &linelen))
+               if (!linelen || is_blank_line(line, &linelen))
                        break;
 
                if (!sb)
@@ -894,11 +896,11 @@ static void parse_commit_message(struct format_commit_context *c)
        const char *msg = c->message + c->message_off;
        const char *start = c->message;
 
-       msg = skip_empty_lines(msg);
+       msg = skip_blank_lines(msg);
        c->subject_off = msg - start;
 
        msg = format_subject(NULL, msg, NULL);
-       msg = skip_empty_lines(msg);
+       msg = skip_blank_lines(msg);
        c->body_off = msg - start;
 
        c->commit_message_parsed = 1;
@@ -1621,7 +1623,7 @@ void pp_title_line(struct pretty_print_context *pp,
        if (pp->after_subject) {
                strbuf_addstr(sb, pp->after_subject);
        }
-       if (pp->fmt == CMIT_FMT_EMAIL) {
+       if (cmit_fmt_is_mail(pp->fmt)) {
                strbuf_addch(sb, '\n');
        }
 
@@ -1704,6 +1706,16 @@ static void pp_handle_indent(struct pretty_print_context *pp,
                strbuf_add(sb, line, linelen);
 }
 
+static int is_mboxrd_from(const char *line, int len)
+{
+       /*
+        * a line matching /^From $/ here would only have len == 4
+        * at this point because is_empty_line would've trimmed all
+        * trailing space
+        */
+       return len > 4 && starts_with(line + strspn(line, ">"), "From ");
+}
+
 void pp_remainder(struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
@@ -1718,7 +1730,7 @@ void pp_remainder(struct pretty_print_context *pp,
                if (!linelen)
                        break;
 
-               if (is_empty_line(line, &linelen)) {
+               if (is_blank_line(line, &linelen)) {
                        if (first)
                                continue;
                        if (pp->fmt == CMIT_FMT_SHORT)
@@ -1732,8 +1744,13 @@ void pp_remainder(struct pretty_print_context *pp,
                else if (pp->expand_tabs_in_log)
                        strbuf_add_tabexpand(sb, pp->expand_tabs_in_log,
                                             line, linelen);
-               else
+               else {
+                       if (pp->fmt == CMIT_FMT_MBOXRD &&
+                                       is_mboxrd_from(line, linelen))
+                               strbuf_addch(sb, '>');
+
                        strbuf_add(sb, line, linelen);
+               }
                strbuf_addch(sb, '\n');
        }
 }
@@ -1757,14 +1774,14 @@ void pretty_print_commit(struct pretty_print_context *pp,
        encoding = get_log_output_encoding();
        msg = reencoded = logmsg_reencode(commit, NULL, encoding);
 
-       if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
+       if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
                indent = 0;
 
        /*
         * We need to check and emit Content-type: to mark it
         * as 8-bit if we haven't done so.
         */
-       if (pp->fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
+       if (cmit_fmt_is_mail(pp->fmt) && need_8bit_cte == 0) {
                int i, ch, in_body;
 
                for (in_body = i = 0; (ch = msg[i]); i++) {
@@ -1789,10 +1806,10 @@ void pretty_print_commit(struct pretty_print_context *pp,
        }
 
        /* Skip excess blank lines at the beginning of body, if any... */
-       msg = skip_empty_lines(msg);
+       msg = skip_blank_lines(msg);
 
        /* These formats treat the title line specially. */
-       if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
+       if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
                pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
 
        beginning_of_body = sb->len;
@@ -1809,7 +1826,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
         * format.  Make sure we did not strip the blank line
         * between the header and the body.
         */
-       if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
+       if (cmit_fmt_is_mail(pp->fmt) && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
 
        unuse_commit_buffer(commit, reencoded);
index d30d1c4f802c77a47df0078f49247457bf22c1a3..edba5b79bc1435483ee2555e1ba645743721ac5d 100644 (file)
@@ -1425,7 +1425,7 @@ static void prepare_show_merge(struct rev_info *revs)
                       ce_same_name(ce, active_cache[i+1]))
                        i++;
        }
-       free_pathspec(&revs->prune_data);
+       clear_pathspec(&revs->prune_data);
        parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
                       PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH, "", prune);
        revs->limited = 1;
@@ -1871,6 +1871,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->notes_opt.use_default_notes = 1;
        } else if (!strcmp(arg, "--show-signature")) {
                revs->show_signature = 1;
+       } else if (!strcmp(arg, "--no-show-signature")) {
+               revs->show_signature = 0;
        } else if (!strcmp(arg, "--show-linear-break") ||
                   starts_with(arg, "--show-linear-break=")) {
                if (starts_with(arg, "--show-linear-break="))
index af0c8a10df0ca06e900d26486fa6c1a86bb2ff7f..33bc63a1de8d41b52a0ec353cf80b0f2e65dc8fd 100644 (file)
@@ -864,19 +864,161 @@ int run_hook_le(const char *const *env, const char *name, ...)
        return ret;
 }
 
-int capture_command(struct child_process *cmd, struct strbuf *buf, size_t hint)
+struct io_pump {
+       /* initialized by caller */
+       int fd;
+       int type; /* POLLOUT or POLLIN */
+       union {
+               struct {
+                       const char *buf;
+                       size_t len;
+               } out;
+               struct {
+                       struct strbuf *buf;
+                       size_t hint;
+               } in;
+       } u;
+
+       /* returned by pump_io */
+       int error; /* 0 for success, otherwise errno */
+
+       /* internal use */
+       struct pollfd *pfd;
+};
+
+static int pump_io_round(struct io_pump *slots, int nr, struct pollfd *pfd)
+{
+       int pollsize = 0;
+       int i;
+
+       for (i = 0; i < nr; i++) {
+               struct io_pump *io = &slots[i];
+               if (io->fd < 0)
+                       continue;
+               pfd[pollsize].fd = io->fd;
+               pfd[pollsize].events = io->type;
+               io->pfd = &pfd[pollsize++];
+       }
+
+       if (!pollsize)
+               return 0;
+
+       if (poll(pfd, pollsize, -1) < 0) {
+               if (errno == EINTR)
+                       return 1;
+               die_errno("poll failed");
+       }
+
+       for (i = 0; i < nr; i++) {
+               struct io_pump *io = &slots[i];
+
+               if (io->fd < 0)
+                       continue;
+
+               if (!(io->pfd->revents & (POLLOUT|POLLIN|POLLHUP|POLLERR|POLLNVAL)))
+                       continue;
+
+               if (io->type == POLLOUT) {
+                       ssize_t len = xwrite(io->fd,
+                                            io->u.out.buf, io->u.out.len);
+                       if (len < 0) {
+                               io->error = errno;
+                               close(io->fd);
+                               io->fd = -1;
+                       } else {
+                               io->u.out.buf += len;
+                               io->u.out.len -= len;
+                               if (!io->u.out.len) {
+                                       close(io->fd);
+                                       io->fd = -1;
+                               }
+                       }
+               }
+
+               if (io->type == POLLIN) {
+                       ssize_t len = strbuf_read_once(io->u.in.buf,
+                                                      io->fd, io->u.in.hint);
+                       if (len < 0)
+                               io->error = errno;
+                       if (len <= 0) {
+                               close(io->fd);
+                               io->fd = -1;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+static int pump_io(struct io_pump *slots, int nr)
+{
+       struct pollfd *pfd;
+       int i;
+
+       for (i = 0; i < nr; i++)
+               slots[i].error = 0;
+
+       ALLOC_ARRAY(pfd, nr);
+       while (pump_io_round(slots, nr, pfd))
+               ; /* nothing */
+       free(pfd);
+
+       /* There may be multiple errno values, so just pick the first. */
+       for (i = 0; i < nr; i++) {
+               if (slots[i].error) {
+                       errno = slots[i].error;
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+
+int pipe_command(struct child_process *cmd,
+                const char *in, size_t in_len,
+                struct strbuf *out, size_t out_hint,
+                struct strbuf *err, size_t err_hint)
 {
-       cmd->out = -1;
+       struct io_pump io[3];
+       int nr = 0;
+
+       if (in)
+               cmd->in = -1;
+       if (out)
+               cmd->out = -1;
+       if (err)
+               cmd->err = -1;
+
        if (start_command(cmd) < 0)
                return -1;
 
-       if (strbuf_read(buf, cmd->out, hint) < 0) {
-               close(cmd->out);
+       if (in) {
+               io[nr].fd = cmd->in;
+               io[nr].type = POLLOUT;
+               io[nr].u.out.buf = in;
+               io[nr].u.out.len = in_len;
+               nr++;
+       }
+       if (out) {
+               io[nr].fd = cmd->out;
+               io[nr].type = POLLIN;
+               io[nr].u.in.buf = out;
+               io[nr].u.in.hint = out_hint;
+               nr++;
+       }
+       if (err) {
+               io[nr].fd = cmd->err;
+               io[nr].type = POLLIN;
+               io[nr].u.in.buf = err;
+               io[nr].u.in.hint = err_hint;
+               nr++;
+       }
+
+       if (pump_io(io, nr) < 0) {
                finish_command(cmd); /* throw away exit code */
                return -1;
        }
 
-       close(cmd->out);
        return finish_command(cmd);
 }
 
index 11f76b04edf65fe6117604782c220e55b2d80661..50666497aeca8f4d3d403cfaeb0afeb350216a42 100644 (file)
@@ -79,17 +79,34 @@ int run_command_v_opt(const char **argv, int opt);
 int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
 
 /**
- * Execute the given command, capturing its stdout in the given strbuf.
+ * Execute the given command, sending "in" to its stdin, and capturing its
+ * stdout and stderr in the "out" and "err" strbufs. Any of the three may
+ * be NULL to skip processing.
+ *
  * Returns -1 if starting the command fails or reading fails, and otherwise
- * returns the exit code of the command. The output collected in the
- * buffer is kept even if the command returns a non-zero exit. The hint field
- * gives a starting size for the strbuf allocation.
+ * returns the exit code of the command. Any output collected in the
+ * buffers is kept even if the command returns a non-zero exit. The hint fields
+ * gives starting sizes for the strbuf allocations.
  *
  * The fields of "cmd" should be set up as they would for a normal run_command
- * invocation. But note that there is no need to set cmd->out; the function
- * sets it up for the caller.
+ * invocation. But note that there is no need to set the in, out, or err
+ * fields; pipe_command handles that automatically.
+ */
+int pipe_command(struct child_process *cmd,
+                const char *in, size_t in_len,
+                struct strbuf *out, size_t out_hint,
+                struct strbuf *err, size_t err_hint);
+
+/**
+ * Convenience wrapper around pipe_command for the common case
+ * of capturing only stdout.
  */
-int capture_command(struct child_process *cmd, struct strbuf *buf, size_t hint);
+static inline int capture_command(struct child_process *cmd,
+                                 struct strbuf *out,
+                                 size_t hint)
+{
+       return pipe_command(cmd, NULL, 0, out, hint, NULL, 0);
+}
 
 /*
  * The purpose of the following functions is to feed a pipe by running
index 37ee04ea3bf41b3bc4b0b920d52449c2f6d681d7..299d303848384935000e60505be414c218eca0cd 100644 (file)
@@ -36,18 +36,15 @@ int option_parse_push_signed(const struct option *opt,
        die("bad %s argument: %s", opt->long_name, arg);
 }
 
-static int feed_object(const unsigned char *sha1, int fd, int negative)
+static void feed_object(const unsigned char *sha1, FILE *fh, int negative)
 {
-       char buf[42];
-
        if (negative && !has_sha1_file(sha1))
-               return 1;
+               return;
 
-       memcpy(buf + negative, sha1_to_hex(sha1), 40);
        if (negative)
-               buf[0] = '^';
-       buf[40 + negative] = '\n';
-       return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
+               putc('^', fh);
+       fputs(sha1_to_hex(sha1), fh);
+       putc('\n', fh);
 }
 
 /*
@@ -73,6 +70,7 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
                NULL,
        };
        struct child_process po = CHILD_PROCESS_INIT;
+       FILE *po_in;
        int i;
 
        i = 4;
@@ -97,21 +95,22 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
         * We feed the pack-objects we just spawned with revision
         * parameters by writing to the pipe.
         */
+       po_in = xfdopen(po.in, "w");
        for (i = 0; i < extra->nr; i++)
-               if (!feed_object(extra->sha1[i], po.in, 1))
-                       break;
+               feed_object(extra->sha1[i], po_in, 1);
 
        while (refs) {
-               if (!is_null_oid(&refs->old_oid) &&
-                   !feed_object(refs->old_oid.hash, po.in, 1))
-                       break;
-               if (!is_null_oid(&refs->new_oid) &&
-                   !feed_object(refs->new_oid.hash, po.in, 0))
-                       break;
+               if (!is_null_oid(&refs->old_oid))
+                       feed_object(refs->old_oid.hash, po_in, 1);
+               if (!is_null_oid(&refs->new_oid))
+                       feed_object(refs->new_oid.hash, po_in, 0);
                refs = refs->next;
        }
 
-       close(po.in);
+       fflush(po_in);
+       if (ferror(po_in))
+               die_errno("error writing to pack-objects");
+       fclose(po_in);
 
        if (args->stateless_rpc) {
                char *buf = xmalloc(LARGE_PACKET_MAX);
index c6362d63f35fe37d67fa235784ab8ce75a60db44..a33c39b64fbd9cf42e5bd531488f7d2366bd167b 100644 (file)
@@ -544,10 +544,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                 * information followed by "\n\n".
                 */
                p = strstr(msg.message, "\n\n");
-               if (p) {
-                       p += 2;
-                       strbuf_addstr(&msgbuf, p);
-               }
+               if (p)
+                       strbuf_addstr(&msgbuf, skip_blank_lines(p + 2));
 
                if (opts->record_origin) {
                        if (!has_conforming_footer(&msgbuf, NULL, 0))
index fde8adc000f3167bba7fdf2d7cb3a4e7db612af4..c09929dafa5843698b0c5c6cb36e70da03ca63a5 100644 (file)
@@ -124,9 +124,8 @@ int recv_sideband(const char *me, int in_stream, int out)
  * fd is connected to the remote side; send the sideband data
  * over multiplexed packet stream.
  */
-ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
+void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
 {
-       ssize_t ssz = sz;
        const char *p = data;
 
        while (sz) {
@@ -148,5 +147,4 @@ ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet
                p += n;
                sz -= n;
        }
-       return ssz;
 }
index e46bed0b0158c0253bacb2a3db028770ad221666..7a8146f161b7b460d29baea82230c0b6c11c5322 100644 (file)
@@ -5,6 +5,6 @@
 #define SIDEBAND_REMOTE_ERROR -1
 
 int recv_sideband(const char *me, int in_stream, int out);
-ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
+void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
 
 #endif
index debab294d421675cdebb129b257808614f607a09..db1847ff6893de6329bf60729b72e8f6a33188b5 100644 (file)
@@ -199,6 +199,7 @@ static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache,
        submodule->update_strategy.command = NULL;
        submodule->fetch_recurse = RECURSE_SUBMODULES_NONE;
        submodule->ignore = NULL;
+       submodule->recommend_shallow = -1;
 
        hashcpy(submodule->gitmodules_sha1, gitmodules_sha1);
 
@@ -353,6 +354,14 @@ static int parse_config(const char *var, const char *value, void *data)
                else if (parse_submodule_update_strategy(value,
                         &submodule->update_strategy) < 0)
                                die(_("invalid value for %s"), var);
+       } else if (!strcmp(item.buf, "shallow")) {
+               if (!me->overwrite && submodule->recommend_shallow != -1)
+                       warn_multiple_config(me->commit_sha1, submodule->name,
+                                            "shallow");
+               else {
+                       submodule->recommend_shallow =
+                               git_config_bool(var, value);
+               }
        }
 
        strbuf_release(&name);
index e4857f53a87d4b8316d53c5f90bc47286179d2c5..b1fdcc0c33326f556d23ce65ad21b01514fcb927 100644 (file)
@@ -18,6 +18,7 @@ struct submodule {
        struct submodule_update_strategy update_strategy;
        /* the sha1 blob id of the responsible .gitmodules file */
        unsigned char gitmodules_sha1[20];
+       int recommend_shallow;
 };
 
 int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
index 4532b11d66f8cdc0abdf003b3a16a632bd923890..abc2ac2a1014e27b9dd56df178cadda6a88c1270 100644 (file)
@@ -17,7 +17,7 @@
 
 static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
 static int parallel_jobs = 1;
-static struct string_list changed_submodule_paths;
+static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP;
 static int initialized_fetch_ref_tips;
 static struct sha1_array ref_tips_before_fetch;
 static struct sha1_array ref_tips_after_fetch;
index 6a775522105d9bfc5c1c60f36944bc43e64badef..509aeef400e6495ce7ca428d7df5cd63f3aa1247 100644 (file)
@@ -25,6 +25,9 @@
  *                             ascending order of priority from a config_set
  *                             constructed from files entered as arguments.
  *
+ * iterate -> iterate over all values using git_config(), and print some
+ *            data for each
+ *
  * Examples:
  *
  * To print the value with highest priority for key "foo.bAr Baz.rock":
  *
  */
 
+static const char *scope_name(enum config_scope scope)
+{
+       switch (scope) {
+       case CONFIG_SCOPE_SYSTEM:
+               return "system";
+       case CONFIG_SCOPE_GLOBAL:
+               return "global";
+       case CONFIG_SCOPE_REPO:
+               return "repo";
+       case CONFIG_SCOPE_CMDLINE:
+               return "cmdline";
+       default:
+               return "unknown";
+       }
+}
+static int iterate_cb(const char *var, const char *value, void *data)
+{
+       static int nr;
+
+       if (nr++)
+               putchar('\n');
+
+       printf("key=%s\n", var);
+       printf("value=%s\n", value ? value : "(null)");
+       printf("origin=%s\n", current_config_origin_type());
+       printf("name=%s\n", current_config_name());
+       printf("scope=%s\n", scope_name(current_config_scope()));
+
+       return 0;
+}
 
 int main(int argc, char **argv)
 {
@@ -134,6 +167,9 @@ int main(int argc, char **argv)
                        printf("Value not found for \"%s\"\n", argv[2]);
                        goto exit1;
                }
+       } else if (!strcmp(argv[1], "iterate")) {
+               git_config(iterate_cb, NULL);
+               goto exit0;
        }
 
        die("%s: Please check the syntax and the function name", argv[0]);
index 8a1235d03e2daab4b9e90d31c5da6708e5c712f2..2c63298fab10563bcd8c98c3d0b6d563164e2283 100644 (file)
@@ -12,7 +12,7 @@ static int dry_run = 0, quiet = 0;
 static char *string = NULL;
 static char *file = NULL;
 static int ambiguous;
-static struct string_list list;
+static struct string_list list = STRING_LIST_INIT_NODUP;
 
 static struct {
        int called;
index f9f3e5fd82e1b34d87993f750bd05e46e69bd82c..ac2cbee250887759b67898e4a2ce9d9c2708b7bc 100644 (file)
@@ -180,6 +180,7 @@ start_httpd() {
        if test $? -ne 0
        then
                trap 'die' EXIT
+               cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
                test_skip_or_die $GIT_TEST_HTTPD "web server setup failed"
        fi
 }
index 8848c1461909199a5c0bd587df8d9ef652c650ac..49ea4349bedfc53b5b565cf1102c74ccb93ab653 100644 (file)
@@ -115,8 +115,16 @@ After that you will want to use some of the following:
 
 At least one of the first two is required!
 
-You can use test_expect_success as usual.  For actual performance
-tests, use
+You can use test_expect_success as usual. In both test_expect_success
+and in test_perf, running "git" points to the version that is being
+perf-tested. The $MODERN_GIT variable points to the git wrapper for the
+currently checked-out version (i.e., the one that matches the t/perf
+scripts you are running).  This is useful if your setup uses commands
+that only work with newer versions of git than what you might want to
+test (but obviously your new commands must still create a state that can
+be used by the older version of git you are testing).
+
+For actual performance tests, use
 
        test_perf 'descriptive string' '
                command1 &&
index 3d074b0e413a0cb5b3df58296385cfdfbefbcaf7..b7ff68d4fa3de5559af672795191a0b81deaa4f8 100755 (executable)
@@ -23,11 +23,11 @@ test_perf 'git log --follow (baseline for -M)' '
        git log --oneline --follow -- "$file" >/dev/null
 '
 
-test_perf 'git log -L' '
-       git log -L 1:"$file" >/dev/null
+test_perf 'git log -L (renames off)' '
+       git log --no-renames -L 1:"$file" >/dev/null
 '
 
-test_perf 'git log -M -L' '
+test_perf 'git log -L (renames on)' '
        git log -M -L 1:"$file" >/dev/null
 '
 
index 773f955d4a251e768b8bc39f7e05161936fda1f5..46f08ee0870ffd2b1e0eb7d159b159d25ef61ffb 100644 (file)
@@ -52,6 +52,9 @@ TEST_NO_MALLOC_CHECK=t
 # need to export them for test_perf subshells
 export TEST_DIRECTORY TRASH_DIRECTORY GIT_BUILD_DIR GIT_TEST_CMP
 
+MODERN_GIT=$GIT_BUILD_DIR/bin-wrappers/git
+export MODERN_GIT
+
 perf_results_dir=$TEST_OUTPUT_DIRECTORY/test-results
 mkdir -p "$perf_results_dir"
 rm -f "$perf_results_dir"/$(basename "$0" .sh).subtests
@@ -81,7 +84,7 @@ test_perf_create_repo_from () {
        repo="$1"
        source="$2"
        source_git="$(git -C "$source" rev-parse --git-dir)"
-       objects_dir="$(git -C "$source" rev-parse --git-path objects)"
+       objects_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-path objects)"
        mkdir -p "$repo/.git"
        (
                cd "$source" &&
index 005d66dbef6c361363752b37087bbbc6a3098833..cf716b469f10f37a591b32ef2d3eb6da4ba63f78 100755 (executable)
@@ -229,4 +229,31 @@ test_expect_success 'error on modifying repo config without repo' '
        )
 '
 
+cmdline_config="'foo.bar=from-cmdline'"
+test_expect_success 'iteration shows correct origins' '
+       echo "[foo]bar = from-repo" >.git/config &&
+       echo "[foo]bar = from-home" >.gitconfig &&
+       cat >expect <<-EOF &&
+       key=foo.bar
+       value=from-home
+       origin=file
+       name=$HOME/.gitconfig
+       scope=global
+
+       key=foo.bar
+       value=from-repo
+       origin=file
+       name=.git/config
+       scope=repo
+
+       key=foo.bar
+       value=from-cmdline
+       origin=command line
+       name=
+       scope=cmdline
+       EOF
+       GIT_CONFIG_PARAMETERS=$cmdline_config test-config iterate >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 3a22fc55fc324fbfbfdcdf39fb71e829c89729b5..4bcc335a19f9d7864560945ed0f430728d0f1986 100755 (executable)
@@ -20,6 +20,22 @@ test_expect_success '"add" an existing empty worktree' '
        git worktree add --detach existing_empty master
 '
 
+test_expect_success '"add" using shorthand - fails when no previous branch' '
+       test_must_fail git worktree add existing_short -
+'
+
+test_expect_success '"add" using - shorthand' '
+       git checkout -b newbranch &&
+       echo hello >myworld &&
+       git add myworld &&
+       git commit -m myworld &&
+       git checkout master &&
+       git worktree add short-hand - &&
+       echo refs/heads/newbranch >expect &&
+       git -C short-hand rev-parse --symbolic-full-name HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success '"add" refuses to checkout locked branch' '
        test_must_fail git worktree add zere master &&
        ! test -d zere &&
index 805dc9012d5f765eb40a67e58c1572b1fe316bd6..1206c48392c36db04ef422813e76b370eeef20b2 100755 (executable)
@@ -1565,4 +1565,45 @@ test_expect_success 'format-patch --base overrides format.useAutoBase' '
        test_cmp expected actual
 '
 
+test_expect_success 'format-patch --pretty=mboxrd' '
+       sp=" " &&
+       cat >msg <<-INPUT_END &&
+       mboxrd should escape the body
+
+       From could trip up a loose mbox parser
+       >From extra escape for reversibility
+       >>From extra escape for reversibility 2
+       from lower case not escaped
+       Fromm bad speling not escaped
+        From with leading space not escaped
+
+       F
+       From
+       From$sp
+       From    $sp
+       From    $sp
+       INPUT_END
+
+       cat >expect <<-INPUT_END &&
+       >From could trip up a loose mbox parser
+       >>From extra escape for reversibility
+       >>>From extra escape for reversibility 2
+       from lower case not escaped
+       Fromm bad speling not escaped
+        From with leading space not escaped
+
+       F
+       From
+       From
+       From
+       From
+       INPUT_END
+
+       C=$(git commit-tree HEAD^^{tree} -p HEAD <msg) &&
+       git format-patch --pretty=mboxrd --stdout -1 $C~1..$C >patch &&
+       git grep -h --no-index -A11 \
+               "^>From could trip up a loose mbox parser" patch >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 67373dc44ef18ae4c58f237ed9f5c7610ec6413c..1795ffc3aaf3008f3ef3adc566803a66603975dd 100755 (executable)
@@ -30,6 +30,7 @@ diffpatterns="
        bibtex
        cpp
        csharp
+       css
        fortran
        fountain
        html
diff --git a/t/t4018/css-brace-in-col-1 b/t/t4018/css-brace-in-col-1
new file mode 100644 (file)
index 0000000..7831577
--- /dev/null
@@ -0,0 +1,5 @@
+RIGHT label.control-label
+{
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
diff --git a/t/t4018/css-colon-eol b/t/t4018/css-colon-eol
new file mode 100644 (file)
index 0000000..5a30553
--- /dev/null
@@ -0,0 +1,4 @@
+RIGHT h1 {
+color:
+ChangeMe;
+}
diff --git a/t/t4018/css-colon-selector b/t/t4018/css-colon-selector
new file mode 100644 (file)
index 0000000..c6d71fb
--- /dev/null
@@ -0,0 +1,5 @@
+RIGHT a:hover {
+    margin-top:
+    10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
diff --git a/t/t4018/css-common b/t/t4018/css-common
new file mode 100644 (file)
index 0000000..84ed754
--- /dev/null
@@ -0,0 +1,4 @@
+RIGHT label.control-label {
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
diff --git a/t/t4018/css-long-selector-list b/t/t4018/css-long-selector-list
new file mode 100644 (file)
index 0000000..7ccd25d
--- /dev/null
@@ -0,0 +1,6 @@
+p.header,
+label.control-label,
+div ul#RIGHT {
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
diff --git a/t/t4018/css-prop-sans-indent b/t/t4018/css-prop-sans-indent
new file mode 100644 (file)
index 0000000..a9e3c86
--- /dev/null
@@ -0,0 +1,5 @@
+RIGHT, label.control-label {
+margin-top: 10px!important;
+padding: 0;
+border : 10px ChangeMe #C6C6C6;
+}
diff --git a/t/t4018/css-short-selector-list b/t/t4018/css-short-selector-list
new file mode 100644 (file)
index 0000000..6a0bdee
--- /dev/null
@@ -0,0 +1,4 @@
+label.control, div ul#RIGHT {
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
diff --git a/t/t4018/css-trailing-space b/t/t4018/css-trailing-space
new file mode 100644 (file)
index 0000000..32b5606
--- /dev/null
@@ -0,0 +1,5 @@
+RIGHT label.control-label {
+    margin:10px;   
+    padding:10px;
+    border : 10px ChangeMe #C6C6C6;
+}
index 2b32c4fbe67d9dc0a43fc7858ddd3ac400b6171f..ec78c5e3ac5661977b30582df9bec95265e1c613 100755 (executable)
@@ -50,14 +50,19 @@ test_expect_success 'attr negation' '
        color "nobold nodim noul noblink noreverse" "[22;24;25;27m"
 '
 
+test_expect_success '"no-" variant of negation' '
+       color "no-bold no-blink" "[22;25m"
+'
+
 test_expect_success 'long color specification' '
        color "254 255 bold dim ul blink reverse" "[1;2;4;5;7;38;5;254;48;5;255m"
 '
 
 test_expect_success 'absurdly long color specification' '
        color \
-         "#ffffff #ffffff bold nobold dim nodim ul noul blink noblink reverse noreverse" \
-         "[1;2;4;5;7;22;24;25;27;38;2;255;255;255;48;2;255;255;255m"
+         "#ffffff #ffffff bold nobold dim nodim italic noitalic
+          ul noul blink noblink reverse noreverse strike nostrike" \
+         "[1;2;3;4;5;7;9;22;23;24;25;27;29;38;2;255;255;255;48;2;255;255;255m"
 '
 
 test_expect_success '0-7 are aliases for basic ANSI color names' '
index f2f55fc51ccd294194300d4bbf66307ac73f9ac6..912df91226f2a018016129a6634b837faee19869 100755 (executable)
@@ -302,6 +302,7 @@ test_language_driver ada
 test_language_driver bibtex
 test_language_driver cpp
 test_language_driver csharp
+test_language_driver css
 test_language_driver fortran
 test_language_driver html
 test_language_driver java
diff --git a/t/t4034/css/expect b/t/t4034/css/expect
new file mode 100644 (file)
index 0000000..ed10393
--- /dev/null
@@ -0,0 +1,16 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index b8ae0bb..fe500b7 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,10 +1,10 @@<RESET>
+.<RED>class-form<RESET><GREEN>other-form<RESET> label.control-label {
+    margin-top: <RED>10<RESET><GREEN>15<RESET>px!important;
+    border : 10px <RED>dashed<RESET><GREEN>dotted<RESET> #C6C6C6;
+}<RESET>
+<RED>#CCCCCC<RESET><GREEN>#CCCCCB<RESET>
+10em<RESET>
+<RED>padding-bottom<RESET><GREEN>margin-left<RESET>
+150<RED>px<RESET><GREEN>em<RESET>
+10px
+<RED>!important<RESET>
+<RED>div<RESET><GREEN>li<RESET>.class#id
diff --git a/t/t4034/css/post b/t/t4034/css/post
new file mode 100644 (file)
index 0000000..fe500b7
--- /dev/null
@@ -0,0 +1,10 @@
+.other-form label.control-label {
+    margin-top: 15px!important;
+    border : 10px dotted #C6C6C6;
+}
+#CCCCCB
+10em
+margin-left
+150em
+10px
+li.class#id
diff --git a/t/t4034/css/pre b/t/t4034/css/pre
new file mode 100644 (file)
index 0000000..b8ae0bb
--- /dev/null
@@ -0,0 +1,10 @@
+.class-form label.control-label {
+    margin-top: 10px!important;
+    border : 10px dashed #C6C6C6;
+}
+#CCCCCC
+10em
+padding-bottom
+150px
+10px!important
+div.class#id
index b41bd17264f93d54d5aa280fbc305cebb18950a0..9ce9424d1569a1d788d4ad1ecd59ed49ee712101 100755 (executable)
@@ -957,4 +957,24 @@ test_expect_success 'am -s unexpected trailer block' '
        test_cmp expect actual
 '
 
+test_expect_success 'am --patch-format=mboxrd handles mboxrd' '
+       rm -fr .git/rebase-apply &&
+       git checkout -f first &&
+       echo mboxrd >>file &&
+       git add file &&
+       cat >msg <<-\INPUT_END &&
+       mboxrd should escape the body
+
+       From could trip up a loose mbox parser
+       >From extra escape for reversibility
+       INPUT_END
+       git commit -F msg &&
+       git format-patch --pretty=mboxrd --stdout -1 >mboxrd1 &&
+       grep "^>From could trip up a loose mbox parser" mboxrd1 &&
+       git checkout -f first &&
+       git am --patch-format=mboxrd mboxrd1 &&
+       git cat-file commit HEAD | tail -n4 >out &&
+       test_cmp msg out
+'
+
 test_done
index 128ba935371fe7a35b4fb8b80ba361a086be01f7..803e1e6b8fc5633b4d33c5749226b1aa506a8152 100755 (executable)
@@ -860,12 +860,15 @@ test_expect_success 'dotdot is a parent directory' '
        test_cmp expect actual
 '
 
-test_expect_success GPG 'log --graph --show-signature' '
+test_expect_success GPG 'setup signed branch' '
        test_when_finished "git reset --hard && git checkout master" &&
        git checkout -b signed master &&
        echo foo >foo &&
        git add foo &&
-       git commit -S -m signed_commit &&
+       git commit -S -m signed_commit
+'
+
+test_expect_success GPG 'log --graph --show-signature' '
        git log --graph --show-signature -n1 signed >actual &&
        grep "^| gpg: Signature made" actual &&
        grep "^| gpg: Good signature" actual
@@ -890,6 +893,31 @@ test_expect_success GPG 'log --graph --show-signature for merged tag' '
        grep "^| | gpg: Good signature" actual
 '
 
+test_expect_success GPG '--no-show-signature overrides --show-signature' '
+       git log -1 --show-signature --no-show-signature signed >actual &&
+       ! grep "^gpg:" actual
+'
+
+test_expect_success GPG 'log.showsignature=true behaves like --show-signature' '
+       test_config log.showsignature true &&
+       git log -1 signed >actual &&
+       grep "gpg: Signature made" actual &&
+       grep "gpg: Good signature" actual
+'
+
+test_expect_success GPG '--no-show-signature overrides log.showsignature=true' '
+       test_config log.showsignature true &&
+       git log -1 --no-show-signature signed >actual &&
+       ! grep "^gpg:" actual
+'
+
+test_expect_success GPG '--show-signature overrides log.showsignature=false' '
+       test_config log.showsignature false &&
+       git log -1 --show-signature signed >actual &&
+       grep "gpg: Signature made" actual &&
+       grep "gpg: Good signature" actual
+'
+
 test_expect_success 'log --graph --no-walk is forbidden' '
        test_must_fail git log --graph --no-walk
 '
index 85b3df5e339b668688873c27244c458443bb6e6a..1a5a546230b9d971594af754fa5f11e5decd3cef 100755 (executable)
@@ -111,4 +111,35 @@ test_expect_success 'mailinfo on message with quoted >From' '
        test_cmp "$TEST_DIRECTORY"/t5100/quoted-from.expect quoted-from/msg
 '
 
+test_expect_success 'mailinfo unescapes with --mboxrd' '
+       mkdir mboxrd &&
+       git mailsplit -omboxrd --mboxrd \
+               "$TEST_DIRECTORY"/t5100/sample.mboxrd >last &&
+       test x"$(cat last)" = x2 &&
+       for i in 0001 0002
+       do
+               git mailinfo mboxrd/msg mboxrd/patch \
+                 <mboxrd/$i >mboxrd/out &&
+               test_cmp "$TEST_DIRECTORY"/t5100/${i}mboxrd mboxrd/msg
+       done &&
+       sp=" " &&
+       echo "From " >expect &&
+       echo "From " >>expect &&
+       echo >> expect &&
+       cat >sp <<-INPUT_END &&
+       From mboxrd Mon Sep 17 00:00:00 2001
+       From: trailing spacer <sp@example.com>
+       Subject: [PATCH] a commit with trailing space
+
+       From$sp
+       >From$sp
+
+       INPUT_END
+
+       git mailsplit -f2 -omboxrd --mboxrd <sp >last &&
+       test x"$(cat last)" = x1 &&
+       git mailinfo mboxrd/msg mboxrd/patch <mboxrd/0003 &&
+       test_cmp expect mboxrd/msg
+'
+
 test_done
diff --git a/t/t5100/0001mboxrd b/t/t5100/0001mboxrd
new file mode 100644 (file)
index 0000000..494ec55
--- /dev/null
@@ -0,0 +1,4 @@
+From the beginning, mbox should have been mboxrd
+>From escaped
+From not mangled but this line should have been escaped
+
diff --git a/t/t5100/0002mboxrd b/t/t5100/0002mboxrd
new file mode 100644 (file)
index 0000000..71343d4
--- /dev/null
@@ -0,0 +1,5 @@
+ >From unchanged
+ From also unchanged
+no trailing space, no escaping necessary and '>' was intended:
+>From
+
diff --git a/t/t5100/sample.mboxrd b/t/t5100/sample.mboxrd
new file mode 100644 (file)
index 0000000..79ad5ae
--- /dev/null
@@ -0,0 +1,19 @@
+From mboxrd Mon Sep 17 00:00:00 2001
+From: mboxrd writer <mboxrd@example.com>
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: [PATCH] a commit with escaped From lines
+
+>From the beginning, mbox should have been mboxrd
+>>From escaped
+From not mangled but this line should have been escaped
+
+From mboxrd Mon Sep 17 00:00:00 2001
+From: mboxrd writer <mboxrd@example.com>
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: [PATCH 2/2] another with fake From lines
+
+ >From unchanged
+ From also unchanged
+no trailing space, no escaping necessary and '>' was intended:
+>From
+
index 739c089d500f76cbf726f0083062420a51874fc1..3159956fde89639354fe5ed4d3cdf083a2cbbb0f 100755 (executable)
@@ -341,6 +341,22 @@ test_expect_success 'branch.to-rebase.rebase should override pull.rebase' '
        test new = "$(git show HEAD:file2)"
 '
 
+test_expect_success "pull --rebase warns on --verify-signatures" '
+       git reset --hard before-rebase &&
+       git pull --rebase --verify-signatures . copy 2>err &&
+       test "$(git rev-parse HEAD^)" = "$(git rev-parse copy)" &&
+       test new = "$(git show HEAD:file2)" &&
+       test_i18ngrep "ignoring --verify-signatures for rebase" err
+'
+
+test_expect_success "pull --rebase does not warn on --no-verify-signatures" '
+       git reset --hard before-rebase &&
+       git pull --rebase --no-verify-signatures . copy 2>err &&
+       test "$(git rev-parse HEAD^)" = "$(git rev-parse copy)" &&
+       test new = "$(git show HEAD:file2)" &&
+       test_i18ngrep ! "verify-signatures" err
+'
+
 # add a feature branch, keep-merge, that is merged into master, so the
 # test can try preserving the merge commit (or not) with various
 # --rebase flags/pull.rebase settings.
diff --git a/t/t5544-pack-objects-hook.sh b/t/t5544-pack-objects-hook.sh
new file mode 100755 (executable)
index 0000000..4357af1
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='test custom script in place of pack-objects'
+. ./test-lib.sh
+
+test_expect_success 'create some history to fetch' '
+       test_commit one &&
+       test_commit two
+'
+
+test_expect_success 'create debugging hook script' '
+       write_script .git/hook <<-\EOF
+               echo >&2 "hook running"
+               echo "$*" >hook.args
+               cat >hook.stdin
+               "$@" <hook.stdin >hook.stdout
+               cat hook.stdout
+       EOF
+'
+
+clear_hook_results () {
+       rm -rf .git/hook.* dst.git
+}
+
+test_expect_success 'hook runs via global config' '
+       clear_hook_results &&
+       test_config_global uploadpack.packObjectsHook ./hook &&
+       git clone --no-local . dst.git 2>stderr &&
+       grep "hook running" stderr
+'
+
+test_expect_success 'hook outputs are sane' '
+       # check that we recorded a usable pack
+       git index-pack --stdin <.git/hook.stdout &&
+
+       # check that we recorded args and stdin. We do not check
+       # the full argument list or the exact pack contents, as it would make
+       # the test brittle. So just sanity check that we could replay
+       # the packing procedure.
+       grep "^git" .git/hook.args &&
+       $(cat .git/hook.args) <.git/hook.stdin >replay
+'
+
+test_expect_success 'hook runs from -c config' '
+       clear_hook_results &&
+       git clone --no-local \
+         -u "git -c uploadpack.packObjectsHook=./hook upload-pack" \
+         . dst.git 2>stderr &&
+       grep "hook running" stderr
+'
+
+test_expect_success 'hook does not run from repo config' '
+       clear_hook_results &&
+       test_config uploadpack.packObjectsHook "./hook" &&
+       git clone --no-local . dst.git 2>stderr &&
+       ! grep "hook running" stderr &&
+       test_path_is_missing .git/hook.args &&
+       test_path_is_missing .git/hook.stdin &&
+       test_path_is_missing .git/hook.stdout
+'
+
+test_done
index da2a67f656beb5178d08597044fa8abb7c831863..a87d329656da6685e9d0846714f20a5aeeaa3556 100755 (executable)
@@ -67,4 +67,56 @@ test_expect_success 'non shallow clone with shallow submodule' '
        test_line_count = 1 lines
 '
 
+test_expect_success 'clone follows shallow recommendation' '
+       test_when_finished "rm -rf super_clone" &&
+       git config -f .gitmodules submodule.sub.shallow true &&
+       git add .gitmodules &&
+       git commit -m "recommed shallow for sub" &&
+       git clone --recurse-submodules --no-local "file://$pwd/." super_clone &&
+       (
+               cd super_clone &&
+               git log --oneline >lines &&
+               test_line_count = 4 lines
+       ) &&
+       (
+               cd super_clone/sub &&
+               git log --oneline >lines &&
+               test_line_count = 1 lines
+       )
+'
+
+test_expect_success 'get unshallow recommended shallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --no-local "file://$pwd/." super_clone &&
+       (
+               cd super_clone &&
+               git submodule update --init --no-recommend-shallow &&
+               git log --oneline >lines &&
+               test_line_count = 4 lines
+       ) &&
+       (
+               cd super_clone/sub &&
+               git log --oneline >lines &&
+               test_line_count = 3 lines
+       )
+'
+
+test_expect_success 'clone follows non shallow recommendation' '
+       test_when_finished "rm -rf super_clone" &&
+       git config -f .gitmodules submodule.sub.shallow false &&
+       git add .gitmodules &&
+       git commit -m "recommed non shallow for sub" &&
+       git clone --recurse-submodules --no-local "file://$pwd/." super_clone &&
+       (
+               cd super_clone &&
+               git log --oneline >lines &&
+               test_line_count = 5 lines
+       ) &&
+       (
+               cd super_clone/sub &&
+               git log --oneline >lines &&
+               test_line_count = 3 lines
+       )
+'
+
 test_done
index f9b7d79af571ab2c377edeb7d46c1605078a4e02..8b0f71a2ac15d65f977b0daa2a53ad47b64a043a 100755 (executable)
@@ -1202,10 +1202,17 @@ test_expect_success GPG,RFC1991 \
 # try to sign with bad user.signingkey
 git config user.signingkey BobTheMouse
 test_expect_success GPG \
-       'git tag -s fails if gpg is misconfigured' \
+       'git tag -s fails if gpg is misconfigured (bad key)' \
        'test_must_fail git tag -s -m tail tag-gpg-failure'
 git config --unset user.signingkey
 
+# try to produce invalid signature
+test_expect_success GPG \
+       'git tag -s fails if gpg is misconfigured (bad signature format)' \
+       'test_config gpg.program echo &&
+        test_must_fail git tag -s -m tail tag-gpg-failure'
+
+
 # try to verify without gpg:
 
 rm -rf gpghome
index 4177a8609acac433cbc3f87d9ddec9c02672963c..6e839f5489c5a8e3667d182f6de1873c2d29bd0a 100755 (executable)
@@ -210,4 +210,11 @@ test_expect_success GPG 'show lack of signature with custom format' '
        test_cmp expect actual
 '
 
+test_expect_success GPG 'log.showsignature behaves like --show-signature' '
+       test_config log.showsignature true &&
+       git show initial >actual &&
+       grep "gpg: Signature made" actual &&
+       grep "gpg: Good signature" actual
+'
+
 test_done
index b66e3838665ea47d748a7e1a64facd755ee32fb8..987573c41fcd4aee3d5f8fd8aa587c97551c1872 100755 (executable)
@@ -122,4 +122,32 @@ test_expect_success 'keep packed objects found only in index' '
        git cat-file blob :file
 '
 
+test_expect_success 'repack -k keeps unreachable packed objects' '
+       # create packed-but-unreachable object
+       sha1=$(echo unreachable-packed | git hash-object -w --stdin) &&
+       pack=$(echo $sha1 | git pack-objects .git/objects/pack/pack) &&
+       git prune-packed &&
+
+       # -k should keep it
+       git repack -adk &&
+       git cat-file -p $sha1 &&
+
+       # and double check that without -k it would have been removed
+       git repack -ad &&
+       test_must_fail git cat-file -p $sha1
+'
+
+test_expect_success 'repack -k packs unreachable loose objects' '
+       # create loose unreachable object
+       sha1=$(echo would-be-deleted-loose | git hash-object -w --stdin) &&
+       objpath=.git/objects/$(echo $sha1 | sed "s,..,&/,") &&
+       test_path_is_file $objpath &&
+
+       # and confirm that the loose object goes away, but we can
+       # still access it (ergo, it is packed)
+       git repack -adk &&
+       test_path_is_missing $objpath &&
+       git cat-file -p $sha1
+'
+
 test_done
index 29f84a6dd17752aabc76ee3d84d1c6b1064481ba..92c8e792d166ee37d5463de2bce32f61cfa570a1 100755 (executable)
@@ -87,4 +87,21 @@ test_expect_success 'blame --line-porcelain output' '
        test_cmp expect actual
 '
 
+test_expect_success '--porcelain detects first non-blank line as subject' '
+       (
+               GIT_INDEX_FILE=.git/tmp-index &&
+               export GIT_INDEX_FILE &&
+               echo "This is it" >single-file &&
+               git add single-file &&
+               tree=$(git write-tree) &&
+               commit=$(printf "%s\n%s\n%s\n\n\n  \noneline\n\nbody\n" \
+                       "tree $tree" \
+                       "author A <a@b.c> 123456789 +0000" \
+                       "committer C <c@d.e> 123456789 +0000" |
+               git hash-object -w -t commit --stdin) &&
+               git blame --porcelain $commit -- single-file >output &&
+               grep "^summary oneline$" output
+       )
+'
+
 test_done
index 4bca35c2594bbff4a659e1d6d1dcc5e746956a84..74d740de41bbd489dd0ce9fb811f9ea1c08b248c 100755 (executable)
@@ -52,6 +52,7 @@ echo "$@"'
 ###
 
 test_expect_success 'empty stream succeeds' '
+       git config fastimport.unpackLimit 0 &&
        git fast-import </dev/null
 '
 
@@ -2690,6 +2691,7 @@ test_expect_success 'R: blob bigger than threshold' '
        echo >>input &&
 
        test_create_repo R &&
+       git --git-dir=R/.git config fastimport.unpackLimit 0 &&
        git --git-dir=R/.git fast-import --big-file-threshold=1 <input
 '
 
diff --git a/t/t9302-fast-import-unpack-limit.sh b/t/t9302-fast-import-unpack-limit.sh
new file mode 100755 (executable)
index 0000000..a04de14
--- /dev/null
@@ -0,0 +1,105 @@
+#!/bin/sh
+test_description='test git fast-import unpack limit'
+. ./test-lib.sh
+
+test_expect_success 'create loose objects on import' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/master
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       initial
+       COMMIT
+
+       done
+       INPUT_END
+
+       git -c fastimport.unpackLimit=2 fast-import --done <input &&
+       git fsck --no-progress &&
+       test $(find .git/objects/?? -type f | wc -l) -eq 2 &&
+       test $(find .git/objects/pack -type f | wc -l) -eq 0
+'
+
+test_expect_success 'bigger packs are preserved' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/master
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       incremental should create a pack
+       COMMIT
+       from refs/heads/master^0
+
+       commit refs/heads/branch
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       branch
+       COMMIT
+
+       done
+       INPUT_END
+
+       git -c fastimport.unpackLimit=2 fast-import --done <input &&
+       git fsck --no-progress &&
+       test $(find .git/objects/?? -type f | wc -l) -eq 2 &&
+       test $(find .git/objects/pack -type f | wc -l) -eq 2
+'
+
+test_expect_success 'lookups after checkpoint works' '
+       hello_id=$(echo hello | git hash-object --stdin -t blob) &&
+       id="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" &&
+       before=$(git rev-parse refs/heads/master^0) &&
+       (
+               cat <<-INPUT_END &&
+               blob
+               mark :1
+               data 6
+               hello
+
+               commit refs/heads/master
+               mark :2
+               committer $id
+               data <<COMMIT
+               checkpoint after this
+               COMMIT
+               from refs/heads/master^0
+               M 100644 :1 hello
+
+               # pre-checkpoint
+               cat-blob :1
+               cat-blob $hello_id
+               checkpoint
+               # post-checkpoint
+               cat-blob :1
+               cat-blob $hello_id
+               INPUT_END
+
+               n=0 &&
+               from=$before &&
+               while test x"$from" = x"$before"
+               do
+                       if test $n -gt 30
+                       then
+                               echo >&2 "checkpoint did not update branch"
+                               exit 1
+                       else
+                               n=$(($n + 1))
+                       fi &&
+                       sleep 1 &&
+                       from=$(git rev-parse refs/heads/master^0)
+               done &&
+               cat <<-INPUT_END &&
+               commit refs/heads/master
+               committer $id
+               data <<COMMIT
+               make sure from "unpacked sha1 reference" works, too
+               COMMIT
+               from $from
+               INPUT_END
+               echo done
+       ) | git -c fastimport.unpackLimit=100 fast-import --done &&
+       test $(find .git/objects/?? -type f | wc -l) -eq 6 &&
+       test $(find .git/objects/pack -type f | wc -l) -eq 2
+'
+
+test_done
index ebf40f44faab9c41430c259132ca600d7810316b..e164e532b2f76617d822c3bef9492b9525b56e83 100644 (file)
@@ -617,7 +617,7 @@ static void try_to_follow_renames(const unsigned char *old, const unsigned char
        diff_setup_done(&diff_opts);
        ll_diff_tree_sha1(old, new, base, &diff_opts);
        diffcore_std(&diff_opts);
-       free_pathspec(&diff_opts.pathspec);
+       clear_pathspec(&diff_opts.pathspec);
 
        /* Go through the new set of filepairing, and see if we find a more interesting one */
        opt->found_follow = 0;
@@ -640,7 +640,7 @@ static void try_to_follow_renames(const unsigned char *old, const unsigned char
                        /* Update the path we use from now on.. */
                        path[0] = p->one->path;
                        path[1] = NULL;
-                       free_pathspec(&opt->pathspec);
+                       clear_pathspec(&opt->pathspec);
                        parse_pathspec(&opt->pathspec,
                                       PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
                                       PATHSPEC_LITERAL_PATH, "", path);
index f19444df7bc9c19da4d2f3bf831525f7a5d6034d..f93787003a21b465c33ab8302347d80e46c10428 100644 (file)
 #include "sigchain.h"
 #include "version.h"
 #include "string-list.h"
+#include "parse-options.h"
 
-static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
+static const char * const upload_pack_usage[] = {
+       N_("git upload-pack [<options>] <dir>"),
+       NULL
+};
 
 /* Remember to update object flag allocation in object.h */
 #define THEY_HAVE      (1u << 11)
@@ -52,26 +56,28 @@ static int keepalive = 5;
 static int use_sideband;
 static int advertise_refs;
 static int stateless_rpc;
+static const char *pack_objects_hook;
 
 static void reset_timeout(void)
 {
        alarm(timeout);
 }
 
-static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
+static void send_client_data(int fd, const char *data, ssize_t sz)
 {
-       if (use_sideband)
-               return send_sideband(1, fd, data, sz, use_sideband);
+       if (use_sideband) {
+               send_sideband(1, fd, data, sz, use_sideband);
+               return;
+       }
        if (fd == 3)
                /* emergency quit */
                fd = 2;
        if (fd == 2) {
                /* XXX: are we happy to lose stuff here? */
                xwrite(fd, data, sz);
-               return sz;
+               return;
        }
        write_or_die(fd, data, sz);
-       return sz;
 }
 
 static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
@@ -93,6 +99,14 @@ static void create_pack_file(void)
        int i;
        FILE *pipe_fd;
 
+       if (!pack_objects_hook)
+               pack_objects.git_cmd = 1;
+       else {
+               argv_array_push(&pack_objects.args, pack_objects_hook);
+               argv_array_push(&pack_objects.args, "git");
+               pack_objects.use_shell = 1;
+       }
+
        if (shallow_nr) {
                argv_array_push(&pack_objects.args, "--shallow-file");
                argv_array_push(&pack_objects.args, "");
@@ -115,7 +129,6 @@ static void create_pack_file(void)
        pack_objects.in = -1;
        pack_objects.out = -1;
        pack_objects.err = -1;
-       pack_objects.git_cmd = 1;
 
        if (start_command(&pack_objects))
                die("git upload-pack: unable to fork git-pack-objects");
@@ -229,9 +242,7 @@ static void create_pack_file(void)
                        }
                        else
                                buffered = -1;
-                       sz = send_client_data(1, data, sz);
-                       if (sz < 0)
-                               goto fail;
+                       send_client_data(1, data, sz);
                }
 
                /*
@@ -258,9 +269,7 @@ static void create_pack_file(void)
        /* flush the data */
        if (0 <= buffered) {
                data[0] = buffered;
-               sz = send_client_data(1, data, 1);
-               if (sz < 0)
-                       goto fail;
+               send_client_data(1, data, 1);
                fprintf(stderr, "flushed.\n");
        }
        if (use_sideband)
@@ -812,15 +821,28 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
                keepalive = git_config_int(var, value);
                if (!keepalive)
                        keepalive = -1;
+       } else if (current_config_scope() != CONFIG_SCOPE_REPO) {
+               if (!strcmp("uploadpack.packobjectshook", var))
+                       return git_config_string(&pack_objects_hook, var, value);
        }
        return parse_hide_refs_config(var, value, "uploadpack");
 }
 
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
-       char *dir;
-       int i;
+       const char *dir;
        int strict = 0;
+       struct option options[] = {
+               OPT_BOOL(0, "stateless-rpc", &stateless_rpc,
+                        N_("quit after a single request/response exchange")),
+               OPT_BOOL(0, "advertise-refs", &advertise_refs,
+                        N_("exit immediately after intial ref advertisement")),
+               OPT_BOOL(0, "strict", &strict,
+                        N_("do not try <directory>/.git/ if <directory> is no Git directory")),
+               OPT_INTEGER(0, "timeout", &timeout,
+                           N_("interrupt transfer after <n> seconds of inactivity")),
+               OPT_END()
+       };
 
        git_setup_gettext();
 
@@ -828,40 +850,17 @@ int main(int argc, char **argv)
        git_extract_argv0_path(argv[0]);
        check_replace_refs = 0;
 
-       for (i = 1; i < argc; i++) {
-               char *arg = argv[i];
+       argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0);
 
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--advertise-refs")) {
-                       advertise_refs = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--stateless-rpc")) {
-                       stateless_rpc = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--strict")) {
-                       strict = 1;
-                       continue;
-               }
-               if (starts_with(arg, "--timeout=")) {
-                       timeout = atoi(arg+10);
-                       daemon_mode = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-       }
+       if (argc != 1)
+               usage_with_options(upload_pack_usage, options);
 
-       if (i != argc-1)
-               usage(upload_pack_usage);
+       if (timeout)
+               daemon_mode = 1;
 
        setup_path();
 
-       dir = argv[i];
+       dir = argv[0];
 
        if (!enter_repo(dir, strict))
                die("'%s' does not appear to be a git repository", dir);
index 6bf2505994b1d1054604b84d49faeb45eb6a9534..2125d6da26dbdc01f7397415ef1f67616e6f2a71 100644 (file)
@@ -148,6 +148,18 @@ PATTERNS("csharp",
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
         "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
+IPATTERN("css",
+        "![:;][[:space:]]*$\n"
+        "^[_a-z0-9].*$",
+        /* -- */
+        /*
+         * This regex comes from W3C CSS specs. Should theoretically also
+         * allow ISO 10646 characters U+00A0 and higher,
+         * but they are not handled in this regex.
+         */
+        "-?[_a-zA-Z][-_a-zA-Z0-9]*" /* identifiers */
+        "|-?[0-9]+|\\#[0-9a-fA-F]+" /* numbers */
+),
 { "default", NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
index 199b1ef94ba84aad094a5a0fe95360c2df631003..e2a94e04768b7484285ed2aa84259c6818b2cd02 100644 (file)
@@ -153,21 +153,19 @@ static struct worktree *get_linked_worktree(const char *id)
 
 static void mark_current_worktree(struct worktree **worktrees)
 {
-       struct strbuf git_dir = STRBUF_INIT;
-       struct strbuf path = STRBUF_INIT;
+       char *git_dir = xstrdup(absolute_path(get_git_dir()));
        int i;
 
-       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
        for (i = 0; worktrees[i]; i++) {
                struct worktree *wt = worktrees[i];
-               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
-               wt->is_current = !fspathcmp(git_dir.buf, path.buf);
-               strbuf_reset(&path);
-               if (wt->is_current)
+               const char *wt_git_dir = get_worktree_git_dir(wt);
+
+               if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
+                       wt->is_current = 1;
                        break;
+               }
        }
-       strbuf_release(&git_dir);
-       strbuf_release(&path);
+       free(git_dir);
 }
 
 struct worktree **get_worktrees(void)
@@ -189,7 +187,7 @@ struct worktree **get_worktrees(void)
        if (dir) {
                while ((d = readdir(dir)) != NULL) {
                        struct worktree *linked = NULL;
-                       if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+                       if (is_dot_or_dotdot(d->d_name))
                                continue;
 
                        if ((linked = get_linked_worktree(d->d_name))) {
index 49e80aa222132c3c151b79bd37749d92a12f745a..981687945a761a67f11f651eef78c49621df2d59 100644 (file)
@@ -94,14 +94,3 @@ int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg)
 
        return 1;
 }
-
-int write_or_whine(int fd, const void *buf, size_t count, const char *msg)
-{
-       if (write_in_full(fd, buf, count) < 0) {
-               fprintf(stderr, "%s: write error (%s)\n",
-                       msg, strerror(errno));
-               return 0;
-       }
-
-       return 1;
-}