Merge branch 'ps/t1509-chroot-test-fixup'
authorJunio C Hamano <gitster@pobox.com>
Wed, 19 Aug 2015 21:48:52 +0000 (14:48 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Aug 2015 21:48:52 +0000 (14:48 -0700)
t1509 test that requires a dedicated VM environment had some
bitrot, which has been corrected.

* ps/t1509-chroot-test-fixup:
  tests: fix cleanup after tests in t1509-root-worktree
  tests: fix broken && chains in t1509-root-worktree

184 files changed:
Documentation/RelNotes/2.4.8.txt [new file with mode: 0644]
Documentation/RelNotes/2.6.0.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-am.txt
Documentation/git-bisect.txt
Documentation/git-cat-file.txt
Documentation/git-check-ref-format.txt
Documentation/git-checkout.txt
Documentation/git-fast-import.txt
Documentation/git-fsck.txt
Documentation/git-log.txt
Documentation/git-rebase.txt
Documentation/git-reflog.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-tag.txt
Documentation/git-tools.txt
Documentation/git-update-ref.txt
Documentation/git-verify-commit.txt
Documentation/git-verify-tag.txt
Documentation/git-worktree.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitrepository-layout.txt
Documentation/i18n.txt
Documentation/rev-list-options.txt
Documentation/technical/api-argv-array.txt
Documentation/technical/api-parse-options.txt
Documentation/technical/index-format.txt
GIT-VERSION-GEN
Makefile
RelNotes
advice.c
advice.h
archive.c
argv-array.c
argv-array.h
bisect.c
bisect.h
branch.c
branch.h
builtin.h
builtin/add.c
builtin/am.c [new file with mode: 0644]
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/fast-export.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/gc.c
builtin/index-pack.c
builtin/init-db.c
builtin/log.c
builtin/pack-objects.c
builtin/pull.c [new file with mode: 0644]
builtin/receive-pack.c
builtin/reflog.c
builtin/remote.c
builtin/replace.c
builtin/rev-parse.c
builtin/send-pack.c
builtin/shortlog.c
builtin/show-branch.c
builtin/tag.c
builtin/unpack-objects.c
builtin/update-ref.c
builtin/verify-commit.c
builtin/verify-tag.c
builtin/worktree.c
cache-tree.c
cache-tree.h
cache.h
commit-slab.h
commit.c
commit.h
contrib/completion/git-prompt.sh
contrib/examples/git-am.sh [moved from git-am.sh with 98% similarity]
contrib/examples/git-pull.sh [moved from git-pull.sh with 99% similarity]
contrib/subtree/git-subtree.sh
contrib/subtree/t/t7900-subtree.sh
date.c
diff.c
diff.h
dir.c
environment.c
fast-import.c
fsck.c
fsck.h
gettext.c
git-bisect.sh
git-compat-util.h
git-rebase--interactive.sh
git-send-email.perl
git-stash.sh
git.c
gpg-interface.c
gpg-interface.h
http-backend.c
log-tree.c
parse-options-cb.c
parse-options.c
parse-options.h
perl/Git.pm
pkt-line.c
pretty.c
ref-filter.c [new file with mode: 0644]
ref-filter.h [new file with mode: 0644]
reflog-walk.c
reflog-walk.h
refs.c
refs.h
remote-testsvn.c
revision.c
revision.h
setup.c
sha1_name.c
strbuf.c
strbuf.h
submodule.c
t/lib-rebase.sh
t/perf/p7300-clean.sh [new file with mode: 0755]
t/t0002-gitfile.sh
t/t0040-parse-options.sh
t/t0090-cache-tree.sh
t/t1006-cat-file.sh
t/t1090-sparse-checkout-scope.sh [new file with mode: 0755]
t/t1400-update-ref.sh
t/t1402-check-ref-format.sh
t/t1410-reflog.sh
t/t1411-reflog-show.sh
t/t1450-fsck.sh
t/t1502-rev-parse-parseopt.sh
t/t1503-rev-parse-verify.sh
t/t2025-worktree-add.sh
t/t3200-branch.sh
t/t3210-pack-refs.sh
t/t3404-rebase-interactive.sh
t/t3415-rebase-autosquash.sh
t/t3418-rebase-continue.sh
t/t3901-i18n-patch.sh
t/t3903-stash.sh
t/t4018-diff-funcname.sh
t/t4018/fountain-scene [new file with mode: 0644]
t/t4150-am.sh
t/t4151-am-abort.sh
t/t4202-log.sh
t/t5302-pack-index.sh
t/t5312-prune-corruption.sh
t/t5504-fetch-receive-strict.sh
t/t5511-refspec.sh
t/t5520-pull.sh
t/t5601-clone.sh
t/t6030-bisect-porcelain.sh
t/t6300-for-each-ref.sh
t/t6501-freshen-objects.sh
t/t7004-tag.sh
t/t7030-verify-tag.sh [new file with mode: 0755]
t/t7063-status-untracked-cache.sh
t/t7300-clean.sh
t/t7509-commit.sh
t/t7510-signed-commit.sh
t/t7512-status-help.sh
t/t9000-addresses.sh [new file with mode: 0755]
t/t9000/test.pl [new file with mode: 0755]
t/t9001-send-email.sh
t/t9300-fast-import.sh
t/t9903-bash-prompt.sh
test-date.c
test-parse-options.c
test-revision-walking.c
trace.c
trace.h
transport-helper.c
unpack-trees.c
userdiff.c
wrapper.c
wt-status.c

diff --git a/Documentation/RelNotes/2.4.8.txt b/Documentation/RelNotes/2.4.8.txt
new file mode 100644 (file)
index 0000000..ad946b2
--- /dev/null
@@ -0,0 +1,21 @@
+Git v2.4.8 Release Notes
+========================
+
+Fixes since v2.4.7
+------------------
+
+ * Abandoning an already applied change in "git rebase -i" with
+   "--continue" left CHERRY_PICK_HEAD and confused later steps.
+
+ * Various fixes around "git am" that applies a patch to a history
+   that is not there yet.
+
+ * "git for-each-ref" reported "missing object" for 0{40} when it
+   encounters a broken ref.  The lack of object whose name is 0{40} is
+   not the problem; the ref being broken is.
+
+ * "git commit --cleanup=scissors" was not careful enough to protect
+   against getting fooled by a line that looked like scissors.
+
+Also contains typofixes, documentation updates and trivial code
+clean-ups.
diff --git a/Documentation/RelNotes/2.6.0.txt b/Documentation/RelNotes/2.6.0.txt
new file mode 100644 (file)
index 0000000..4a0a589
--- /dev/null
@@ -0,0 +1,190 @@
+Git 2.6 Release Notes
+=====================
+
+Updates since v2.5
+------------------
+
+UI, Workflows & Features
+
+ * An asterisk as a substring (as opposed to the entirety) of a path
+   component for both side of a refspec, e.g.
+   "refs/heads/o*:refs/remotes/heads/i*", is now allowed.
+
+ * New userdiff pattern definition for fountain screenwriting markup
+   format has been added.
+
+ * "git log" and friends learned a new "--date=format:..." option to
+   format timestamps using system's strftime(3).
+
+ * "git fast-import" learned to respond to the get-mark command via
+   its cat-blob-fd interface.
+
+ * "git rebase -i" learned "drop commit-object-name subject" command
+   as another way to skip replaying of a commit.
+
+ * A new configuration variable can enable "--follow" automatically
+   when "git log" is run with one pathspec argument.
+
+ * "git status" learned to show a more detailed information regarding
+   the "rebase -i" session in progress.
+
+ * "git cat-file" learned "--batch-all-objects" option to enumerate all
+   available objects in the repository more quickly than "rev-list
+   --all --objects" (the output includes unreachable objects, though).
+
+ * "git fsck" learned to ignore errors on a set of known-to-be-bad
+   objects, and also allows the warning levels of various kinds of
+   non-critical breakages to be tweaked.
+
+ * "git rebase -i"'s list of todo is made configurable.
+
+ * "git send-email" now performs alias-expansion on names that are
+   given via --cccmd, etc.
+
+ * An environment variable GIT_REPLACE_REF_BASE tells Git to look into
+   refs hierarchy other than refs/replace/ for the object replacement
+   data.
+
+ * Allow untracked cache (experimental) to be used when sparse
+   checkout (experimental) is also in use.
+
+ * "git pull --rebase" has been taught to pay attention to
+   rebase.autostash configuration.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * In preparation for allowing different "backends" to store the refs
+   in a way different from the traditional "one ref per file in
+   $GIT_DIR or in a $GIT_DIR/packed-refs file" filesystem storage,
+   direct filesystem access to ref-like things like CHERRY_PICK_HEAD
+   from scripts and programs has been reduced.
+
+ * Computation of untracked status indicator by bash prompt
+   script (in contrib/) has been optimized.
+
+ * Memory use reduction when commit-slab facility is used to annotate
+   sparsely (which is not recommended in the first place).
+
+ * Clean up refs API and make "git clone" less intimate with the
+   implementation detail.
+
+ * "git pull" was reimplemented in C.
+
+ * The packet tracing machinery allows to capture an incoming pack
+   data to a file for debugging.
+
+ * Move machinery to parse human-readable scaled numbers like 1k, 4M,
+   and 2G as an option parameter's value from pack-objects to
+   parse-options API, to make it available to other codepaths.
+
+ * "git verify-tag" and "git verify-commit" have been taught to share
+   more code, and then learned to optionally show the verification
+   message from the underlying GPG implementation.
+
+ * Various enhancements around "git am" reading patches generated by
+   foreign SCM have been made.
+
+ * Ref listing by "git branch -l" and "git tag -l" commands has
+   started to be rebuilt, based on the for-each-ref machinery.
+
+ * The code to perform multi-tree merges has been taught to repopulate
+   the cache-tree upon a successful merge into the index, so that
+   subsequent "diff-index --cached" (hence "status") and "write-tree"
+   (hence "commit") will go faster.
+
+   The same logic in "git checkout" may now be removed, but that is a
+   separate issue.
+
+ * Tests that assume how reflogs are represented on the filesystem too
+   much have been corrected.
+
+ * "git am" has been rewritten in "C".
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.5
+----------------
+
+Unless otherwise noted, all the fixes since v2.5 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * "git subtree" (in contrib/) depended on "git log" output to be
+   stable, which was a no-no.  Apply a workaround to force a
+   particular date format.
+   (merge e7aac44 da/subtree-date-confusion later to maint).
+
+ * An attempt to delete a ref by pushing into a repositorywhose HEAD
+   symbolic reference points at an unborn branch that cannot be
+   created due to ref D/F conflict (e.g. refs/heads/a/b exists, HEAD
+   points at refs/heads/a) failed.
+   (merge b112b14 jx/do-not-crash-receive-pack-wo-head later to maint).
+
+ * The low-level "git send-pack" did not honor 'user.signingkey'
+   configuration variable when sending a signed-push.
+   (merge d830d39 db/send-pack-user-signingkey later to maint).
+
+ * "sparse checkout" misbehaved for a path that is excluded from the
+   checkout when switching between branches that differ at the path.
+   (merge 7d78241 as/sparse-checkout-removal later to maint).
+
+ * An experimental "untracked cache" feature used uname(2) in a
+   slightly unportable way.
+   (merge 100e433 cb/uname-in-untracked later to maint).
+
+ * A "rebase" replays changes of the local branch on top of something
+   else, as such they are placed in stage #3 and referred to as
+   "theirs", while the changes in the new base, typically a foreign
+   work, are placed in stage #2 and referred to as "ours".  Clarify
+   the "checkout --ours/--theirs".
+   (merge f303016 se/doc-checkout-ours-theirs later to maint).
+
+ * The "rev-parse --parseopt" mode parsed the option specification
+   and the argument hint in a strange way to allow '=' and other
+   special characters in the option name while forbidding them from
+   the argument hint.  This made it impossible to define an option
+   like "--pair <key>=<value>" with "pair=key=value" specification,
+   which instead would have defined a "--pair=key <value>" option.
+   (merge 2d893df ib/scripted-parse-opt-better-hint-string later to maint).
+
+ * Often a fast-import stream builds a new commit on top of the
+   previous commit it built, and it often unconditionally emits a
+   "from" command to specify the first parent, which can be omitted in
+   such a case.  This caused fast-import to forget the tree of the
+   previous commit and then re-read it from scratch, which was
+   inefficient.  Optimize for this common case.
+   (merge 0df3245 mh/fast-import-optimize-current-from later to maint).
+
+ * Running an aliased command from a subdirectory when the .git thing
+   in the working tree is a gitfile pointing elsewhere did not work.
+   (merge d95138e nd/export-worktree later to maint).
+
+ * "Is this subdirectory a separate repository that should not be
+   touched?" check "git clean" was inefficient.  This was replaced
+   with a more optimized check.
+   (merge 38ae878 ee/clean-remove-dirs later to maint).
+
+ * The "new-worktree-mode" hack in "checkout" that was added in
+   nd/multiple-work-trees topic has been removed by updating the
+   implementation of new "worktree add".
+   (merge 65f9b75 es/worktree-add-cleanup later to maint).
+
+ * Remove remaining cruft from  "git checkout --to", which
+   transitioned to "git worktree add".
+   (merge 114ff88 es/worktree-add later to maint).
+
+ * An off-by-one error made "git remote" to mishandle a remote with a
+   single letter nickname.
+   (merge bc598c3 mh/get-remote-group-fix later to maint).
+
+ * Code cleanups and documentation updates.
+   (merge 1c601af es/doc-clean-outdated-tools later to maint).
+   (merge 3581304 kn/tag-doc-fix later to maint).
+   (merge 3a59e59 kb/i18n-doc later to maint).
+   (merge 45abdee sb/remove-unused-var-from-builtin-add later to maint).
+   (merge 14691e3 sb/parse-options-codeformat later to maint).
+   (merge 4a6ada3 ad/bisect-cleanup later to maint).
+   (merge da4c5ad ta/docfix-index-format-tech later to maint).
index 43bb53c..016f6e9 100644 (file)
@@ -769,6 +769,14 @@ am.keepcr::
        by giving '--no-keep-cr' from the command line.
        See linkgit:git-am[1], linkgit:git-mailsplit[1].
 
+am.threeWay::
+       By default, `git am` will fail if the patch does not apply cleanly. When
+       set to true, this setting tells `git am` to fall back on 3-way merge if
+       the patch records the identity of blobs it is supposed to apply to and
+       we have those blobs available locally (equivalent to giving the `--3way`
+       option from the command line). Defaults to `false`.
+       See linkgit:git-am[1].
+
 apply.ignoreWhitespace::
        When set to 'change', tells 'git apply' to ignore changes in
        whitespace, in the same way as the '--ignore-space-change'
@@ -1242,6 +1250,25 @@ filter.<driver>.smudge::
        object to a worktree file upon checkout.  See
        linkgit:gitattributes[5] for details.
 
+fsck.<msg-id>::
+       Allows overriding the message type (error, warn or ignore) of a
+       specific message ID such as `missingEmail`.
++
+For convenience, fsck prefixes the error/warning with the message ID,
+e.g.  "missingEmail: invalid author/committer line - missing email" means
+that setting `fsck.missingEmail = ignore` will hide that issue.
++
+This feature is intended to support working with legacy repositories
+which cannot be repaired without disruptive changes.
+
+fsck.skipList::
+       The path to a sorted list of object names (i.e. one SHA-1 per
+       line) that are known to be broken in a non-fatal way and should
+       be ignored. This feature is useful when an established project
+       should be accepted despite early commits containing errors that
+       can be safely ignored such as invalid committer email addresses.
+       Note: corrupt objects cannot be skipped with this setting.
+
 gc.aggressiveDepth::
        The depth parameter used in the delta compression
        algorithm used by 'git gc --aggressive'.  This defaults
@@ -1280,20 +1307,24 @@ gc.packRefs::
 gc.pruneExpire::
        When 'git gc' is run, it will call 'prune --expire 2.weeks.ago'.
        Override the grace period with this config variable.  The value
-       "now" may be used to disable this  grace period and always prune
-       unreachable objects immediately.
-
-gc.pruneWorktreesExpire::
-       When 'git gc' is run, it will call
-       'prune --worktrees --expire 3.months.ago'.
-       Override the grace period with this config variable. The value
-       "now" may be used to disable the grace period and prune
-       $GIT_DIR/worktrees immediately.
+       "now" may be used to disable this grace period and always prune
+       unreachable objects immediately, or "never" may be used to
+       suppress pruning.
+
+gc.worktreePruneExpire::
+       When 'git gc' is run, it calls
+       'git worktree prune --expire 3.months.ago'.
+       This config variable can be used to set a different grace
+       period. The value "now" may be used to disable the grace
+       period and prune $GIT_DIR/worktrees immediately, or "never"
+       may be used to suppress pruning.
 
 gc.reflogExpire::
 gc.<pattern>.reflogExpire::
        'git reflog expire' removes reflog entries older than
-       this time; defaults to 90 days.  With "<pattern>" (e.g.
+       this time; defaults to 90 days. The value "now" expires all
+       entries immediately, and "never" suppresses expiration
+       altogether. With "<pattern>" (e.g.
        "refs/stash") in the middle the setting applies only to
        the refs that match the <pattern>.
 
@@ -1301,7 +1332,9 @@ gc.reflogExpireUnreachable::
 gc.<ref>.reflogExpireUnreachable::
        'git reflog expire' removes reflog entries older than
        this time and are not reachable from the current tip;
-       defaults to 30 days.  With "<pattern>" (e.g. "refs/stash")
+       defaults to 30 days. The value "now" expires all entries
+       immediately, and "never" suppresses expiration altogether.
+       With "<pattern>" (e.g. "refs/stash")
        in the middle, the setting applies only to the refs that
        match the <pattern>.
 
@@ -2161,6 +2194,22 @@ rebase.autoStash::
        successful rebase might result in non-trivial conflicts.
        Defaults to false.
 
+rebase.missingCommitsCheck::
+       If set to "warn", git rebase -i will print a warning if some
+       commits are removed (e.g. a line was deleted), however the
+       rebase will still proceed. If set to "error", it will print
+       the previous warning and stop the rebase, 'git rebase
+       --edit-todo' can then be used to correct the error. If set to
+       "ignore", no checking is done.
+       To drop a commit without warning or error, use the `drop`
+       command in the todo-list.
+       Defaults to "ignore".
+
+rebase.instructionFormat
+       A format string, as specified in linkgit:git-log[1], to be used for
+       the instruction list during an interactive rebase.  The format will automatically
+       have the long commit hash prepended to the format.
+
 receive.advertiseAtomic::
        By default, git-receive-pack will advertise the atomic push
        capability to its clients. If you don't want to this capability
@@ -2197,6 +2246,28 @@ receive.fsckObjects::
        Defaults to false. If not set, the value of `transfer.fsckObjects`
        is used instead.
 
+receive.fsck.<msg-id>::
+       When `receive.fsckObjects` is set to true, errors can be switched
+       to warnings and vice versa by configuring the `receive.fsck.<msg-id>`
+       setting where the `<msg-id>` is the fsck message ID and the value
+       is one of `error`, `warn` or `ignore`. For convenience, fsck prefixes
+       the error/warning with the message ID, e.g. "missingEmail: invalid
+       author/committer line - missing email" means that setting
+       `receive.fsck.missingEmail = ignore` will hide that issue.
++
+This feature is intended to support working with legacy repositories
+which would not pass pushing when `receive.fsckObjects = true`, allowing
+the host to accept repositories with certain known issues but still catch
+other issues.
+
+receive.fsck.skipList::
+       The path to a sorted list of object names (i.e. one SHA-1 per
+       line) that are known to be broken in a non-fatal way and should
+       be ignored. This feature is useful when an established project
+       should be accepted despite early commits containing errors that
+       can be safely ignored such as invalid committer email addresses.
+       Note: corrupt objects cannot be skipped with this setting.
+
 receive.unpackLimit::
        If the number of objects received in a push is below this
        limit then the objects will be unpacked into loose object
index 0d8ba48..dbea6e7 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8]
-        [--3way] [--interactive] [--committer-date-is-author-date]
+        [--[no-]3way] [--interactive] [--committer-date-is-author-date]
         [--ignore-date] [--ignore-space-change | --ignore-whitespace]
         [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
         [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
@@ -90,10 +90,13 @@ default.   You can use `--no-utf8` to override this.
 
 -3::
 --3way::
+--no-3way::
        When the patch does not apply cleanly, fall back on
        3-way merge if the patch records the identity of blobs
        it is supposed to apply to and we have those blobs
-       available locally.
+       available locally. `--no-3way` can be used to override
+       am.threeWay configuration variable. For more information,
+       see am.threeWay in linkgit:git-config[1].
 
 --ignore-space-change::
 --ignore-whitespace::
index 4cb52a7..e97f2de 100644 (file)
@@ -3,7 +3,7 @@ git-bisect(1)
 
 NAME
 ----
-git-bisect - Find by binary search the change that introduced a bug
+git-bisect - Use binary search to find the commit that introduced a bug
 
 
 SYNOPSIS
@@ -16,7 +16,6 @@ DESCRIPTION
 The command takes various subcommands, and different options depending
 on the subcommand:
 
- git bisect help
  git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
  git bisect bad [<rev>]
  git bisect good [<rev>...]
@@ -26,64 +25,71 @@ on the subcommand:
  git bisect replay <logfile>
  git bisect log
  git bisect run <cmd>...
+ git bisect help
 
-This command uses 'git rev-list --bisect' to help drive the
-binary search process to find which change introduced a bug, given an
-old "good" commit object name and a later "bad" commit object name.
-
-Getting help
-~~~~~~~~~~~~
-
-Use "git bisect" to get a short usage description, and "git bisect
-help" or "git bisect -h" to get a long usage description.
+This command uses a binary search algorithm to find which commit in
+your project's history introduced a bug. You use it by first telling
+it a "bad" commit that is known to contain the bug, and a "good"
+commit that is known to be before the bug was introduced. Then `git
+bisect` picks a commit between those two endpoints and asks you
+whether the selected commit is "good" or "bad". It continues narrowing
+down the range until it finds the exact commit that introduced the
+change.
 
 Basic bisect commands: start, bad, good
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Using the Linux kernel tree as an example, basic use of the bisect
-command is as follows:
+As an example, suppose you are trying to find the commit that broke a
+feature that was known to work in version `v2.6.13-rc2` of your
+project. You start a bisect session as follows:
 
 ------------------------------------------------
 $ git bisect start
 $ git bisect bad                 # Current version is bad
-$ git bisect good v2.6.13-rc2    # v2.6.13-rc2 was the last version
-                                 # tested that was good
+$ git bisect good v2.6.13-rc2    # v2.6.13-rc2 is known to be good
 ------------------------------------------------
 
-When you have specified at least one bad and one good version, the
-command bisects the revision tree and outputs something similar to
-the following:
+Once you have specified at least one bad and one good commit, `git
+bisect` selects a commit in the middle of that range of history,
+checks it out, and outputs something similar to the following:
 
 ------------------------------------------------
-Bisecting: 675 revisions left to test after this
+Bisecting: 675 revisions left to test after this (roughly 10 steps)
 ------------------------------------------------
 
-The state in the middle of the set of revisions is then checked out.
-You would now compile that kernel and boot it. If the booted kernel
-works correctly, you would then issue the following command:
+You should now compile the checked-out version and test it. If that
+version works correctly, type
 
 ------------------------------------------------
-$ git bisect good                      # this one is good
+$ git bisect good
 ------------------------------------------------
 
-The output of this command would be something similar to the following:
+If that version is broken, type
 
 ------------------------------------------------
-Bisecting: 337 revisions left to test after this
+$ git bisect bad
 ------------------------------------------------
 
-You keep repeating this process, compiling the tree, testing it, and
-depending on whether it is good or bad issuing the command "git bisect good"
-or "git bisect bad" to ask for the next bisection.
+Then `git bisect` will respond with something like
+
+------------------------------------------------
+Bisecting: 337 revisions left to test after this (roughly 9 steps)
+------------------------------------------------
+
+Keep repeating the process: compile the tree, test it, and depending
+on whether it is good or bad run `git bisect good` or `git bisect bad`
+to ask for the next commit that needs testing.
+
+Eventually there will be no more revisions left to inspect, and the
+command will print out a description of the first bad commit. The
+reference `refs/bisect/bad` will be left pointing at that commit.
 
-Eventually there will be no more revisions left to bisect, and you
-will have been left with the first bad kernel revision in "refs/bisect/bad".
 
 Bisect reset
 ~~~~~~~~~~~~
 
 After a bisect session, to clean up the bisection state and return to
-the original HEAD (i.e., to quit bisecting), issue the following command:
+the original HEAD, issue the following command:
 
 ------------------------------------------------
 $ git bisect reset
@@ -100,9 +106,10 @@ instead:
 $ git bisect reset <commit>
 ------------------------------------------------
 
-For example, `git bisect reset HEAD` will leave you on the current
-bisection commit and avoid switching commits at all, while `git bisect
-reset bisect/bad` will check out the first bad revision.
+For example, `git bisect reset bisect/bad` will check out the first
+bad revision, while `git bisect reset HEAD` will leave you on the
+current bisection commit and avoid switching commits at all.
+
 
 Bisect visualize
 ~~~~~~~~~~~~~~~~
@@ -147,17 +154,17 @@ $ git bisect replay that-file
 Avoiding testing a commit
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
-If, in the middle of a bisect session, you know that the next suggested
-revision is not a good one to test (e.g. the change the commit
-introduces is known not to work in your environment and you know it
-does not have anything to do with the bug you are chasing), you may
-want to find a nearby commit and try that instead.
+If, in the middle of a bisect session, you know that the suggested
+revision is not a good one to test (e.g. it fails to build and you
+know that the failure does not have anything to do with the bug you
+are chasing), you can manually select a nearby commit and test that
+one instead.
 
 For example:
 
 ------------
 $ git bisect good/bad                  # previous round was good or bad.
-Bisecting: 337 revisions left to test after this
+Bisecting: 337 revisions left to test after this (roughly 9 steps)
 $ git bisect visualize                 # oops, that is uninteresting.
 $ git reset --hard HEAD~3              # try 3 revisions before what
                                        # was suggested
@@ -169,18 +176,19 @@ the revision as good or bad in the usual manner.
 Bisect skip
 ~~~~~~~~~~~~
 
-Instead of choosing by yourself a nearby commit, you can ask Git
-to do it for you by issuing the command:
+Instead of choosing a nearby commit by yourself, you can ask Git to do
+it for you by issuing the command:
 
 ------------
 $ git bisect skip                 # Current version cannot be tested
 ------------
 
-But Git may eventually be unable to tell the first bad commit among
-a bad commit and one or more skipped commits.
+However, if you skip a commit adjacent to the one you are looking for,
+Git will be unable to tell exactly which of those commits was the
+first bad one.
 
-You can even skip a range of commits, instead of just one commit,
-using the "'<commit1>'..'<commit2>'" notation. For example:
+You can also skip a range of commits, instead of just one commit,
+using range notation. For example:
 
 ------------
 $ git bisect skip v2.5..v2.6
@@ -196,8 +204,8 @@ would issue the command:
 $ git bisect skip v2.5 v2.5..v2.6
 ------------
 
-This tells the bisect process that the commits between `v2.5` included
-and `v2.6` included should be skipped.
+This tells the bisect process that the commits between `v2.5` and
+`v2.6` (inclusive) should be skipped.
 
 
 Cutting down bisection by giving more parameters to bisect start
@@ -231,14 +239,14 @@ or bad, you can bisect by issuing the command:
 $ git bisect run my_script arguments
 ------------
 
-Note that the script (`my_script` in the above example) should
-exit with code 0 if the current source code is good, and exit with a
-code between 1 and 127 (inclusive), except 125, if the current
-source code is bad.
+Note that the script (`my_script` in the above example) should exit
+with code 0 if the current source code is good/old, and exit with a
+code between 1 and 127 (inclusive), except 125, if the current source
+code is bad/new.
 
 Any other exit code will abort the bisect process. It should be noted
-that a program that terminates via "exit(-1)" leaves $? = 255, (see the
-exit(3) manual page), as the value is chopped with "& 0377".
+that a program that terminates via `exit(-1)` leaves $? = 255, (see the
+exit(3) manual page), as the value is chopped with `& 0377`.
 
 The special exit code 125 should be used when the current source code
 cannot be tested. If the script exits with this code, the current
@@ -247,7 +255,7 @@ as the highest sensible value to use for this purpose, because 126 and 127
 are used by POSIX shells to signal specific error status (127 is for
 command not found, 126 is for command found but not executable---these
 details do not matter, as they are normal errors in the script, as far as
-"bisect run" is concerned).
+`bisect run` is concerned).
 
 You may often find that during a bisect session you want to have
 temporary modifications (e.g. s/#define DEBUG 0/#define DEBUG 1/ in a
@@ -260,7 +268,7 @@ next revision to test, the script can apply the patch
 before compiling, run the real test, and afterwards decide if the
 revision (possibly with the needed patch) passed the test and then
 rewind the tree to the pristine state.  Finally the script should exit
-with the status of the real test to let the "git bisect run" command loop
+with the status of the real test to let the `git bisect run` command loop
 determine the eventual outcome of the bisect session.
 
 OPTIONS
@@ -307,12 +315,12 @@ $ git bisect run ~/test.sh
 $ git bisect reset                   # quit the bisect session
 ------------
 +
-Here we use a "test.sh" custom script. In this script, if "make"
+Here we use a `test.sh` custom script. In this script, if `make`
 fails, we skip the current commit.
-"check_test_case.sh" should "exit 0" if the test case passes,
-and "exit 1" otherwise.
+`check_test_case.sh` should `exit 0` if the test case passes,
+and `exit 1` otherwise.
 +
-It is safer if both "test.sh" and "check_test_case.sh" are
+It is safer if both `test.sh` and `check_test_case.sh` are
 outside the repository to prevent interactions between the bisect,
 make and test processes and the scripts.
 
@@ -379,6 +387,11 @@ In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit
 has at least one parent whose reachable graph is fully traversable in the sense
 required by 'git pack objects'.
 
+Getting help
+~~~~~~~~~~~~
+
+Use `git bisect` to get a short usage description, and `git bisect
+help` or `git bisect -h` to get a long usage description.
 
 SEE ALSO
 --------
index 319ab4c..3105fc0 100644 (file)
@@ -69,6 +69,20 @@ OPTIONS
        not be combined with any other options or arguments.  See the
        section `BATCH OUTPUT` below for details.
 
+--batch-all-objects::
+       Instead of reading a list of objects on stdin, perform the
+       requested batch operation on all objects in the repository and
+       any alternate object stores (not just reachable objects).
+       Requires `--batch` or `--batch-check` be specified. Note that
+       the objects are visited in order sorted by their hashes.
+
+--buffer::
+       Normally batch output is flushed after each object is output, so
+       that a process can interactively read and write from
+       `cat-file`. With this option, the output uses normal stdio
+       buffering; this is much more efficient when invoking
+       `--batch-check` on a large number of objects.
+
 --allow-unknown-type::
        Allow -s or -t to query broken/corrupt objects of unknown type.
 
index fc02959..9044dfa 100644 (file)
@@ -94,8 +94,8 @@ OPTIONS
        Interpret <refname> as a reference name pattern for a refspec
        (as used with remote repositories).  If this option is
        enabled, <refname> is allowed to contain a single `*`
-       in place of a one full pathname component (e.g.,
-       `foo/*/bar` but not `foo/bar*`).
+       in the refspec (e.g., `foo/bar*/baz` or `foo/bar*baz/`
+       but not `foo/bar*/baz*`).
 
 --normalize::
        Normalize 'refname' by removing any leading slash (`/`)
index 63b739c..e269fb1 100644 (file)
@@ -120,6 +120,21 @@ entries; instead, unmerged entries are ignored.
 --theirs::
        When checking out paths from the index, check out stage #2
        ('ours') or #3 ('theirs') for unmerged paths.
++
+Note that during `git rebase` and `git pull --rebase`, 'ours' and
+'theirs' may appear swapped; `--ours` gives the version from the
+branch the changes are rebased onto, while `--theirs` gives the
+version from the branch that holds your work that is being rebased.
++
+This is because `rebase` is used in a workflow that treats the
+history at the remote as the shared canonical one, and treats the
+work done on the branch you are rebasing as the third-party work to
+be integrated, and you are temporarily assuming the role of the
+keeper of the canonical history during the rebase.  As the keeper of
+the canonical history, you need to view the history from the remote
+as `ours` (i.e. "our shared canonical history"), while what you did
+on your side branch as `theirs` (i.e. "one contributor's work on top
+of it").
 
 -b <new_branch>::
        Create a new branch named <new_branch> and start it at
index fd32895..66910aa 100644 (file)
@@ -54,7 +54,7 @@ Options for Frontends
 ~~~~~~~~~~~~~~~~~~~~~
 
 --cat-blob-fd=<fd>::
-       Write responses to `cat-blob` and `ls` queries to the
+       Write responses to `get-mark`, `cat-blob`, and `ls` queries to the
        file descriptor <fd> instead of `stdout`.  Allows `progress`
        output intended for the end-user to be separated from other
        output.
@@ -350,6 +350,11 @@ and control the current import process.  More detailed discussion
        unless the `done` feature was requested using the
        `--done` command-line option or `feature done` command.
 
+`get-mark`::
+       Causes fast-import to print the SHA-1 corresponding to a mark
+       to the file descriptor set with `--cat-blob-fd`, or `stdout` if
+       unspecified.
+
 `cat-blob`::
        Causes fast-import to print a blob in 'cat-file --batch'
        format to the file descriptor set with `--cat-blob-fd` or
@@ -930,6 +935,25 @@ Placing a `progress` command immediately after a `checkpoint` will
 inform the reader when the `checkpoint` has been completed and it
 can safely access the refs that fast-import updated.
 
+`get-mark`
+~~~~~~~~~~
+Causes fast-import to print the SHA-1 corresponding to a mark to
+stdout or to the file descriptor previously arranged with the
+`--cat-blob-fd` argument. The command otherwise has no impact on the
+current import; its purpose is to retrieve SHA-1s that later commits
+might want to refer to in their commit messages.
+
+....
+       'get-mark' SP ':' <idnum> LF
+....
+
+This command can be used anywhere in the stream that comments are
+accepted.  In particular, the `get-mark` command can be used in the
+middle of a commit but not in the middle of a `data` command.
+
+See ``Responses To Commands'' below for details about how to read
+this output safely.
+
 `cat-blob`
 ~~~~~~~~~~
 Causes fast-import to print a blob to a file descriptor previously
@@ -1000,7 +1024,8 @@ Output uses the same format as `git ls-tree <tree> -- <path>`:
 ====
 
 The <dataref> represents the blob, tree, or commit object at <path>
-and can be used in later 'cat-blob', 'filemodify', or 'ls' commands.
+and can be used in later 'get-mark', 'cat-blob', 'filemodify', or
+'ls' commands.
 
 If there is no file or subtree at that path, 'git fast-import' will
 instead report
@@ -1042,9 +1067,11 @@ import-marks-if-exists::
        "feature import-marks-if-exists" like a corresponding
        command-line option silently skips a nonexistent file.
 
+get-mark::
 cat-blob::
 ls::
-       Require that the backend support the 'cat-blob' or 'ls' command.
+       Require that the backend support the 'get-mark', 'cat-blob',
+       or 'ls' command respectively.
        Versions of fast-import not supporting the specified command
        will exit with a message indicating so.
        This lets the import error out early with a clear message,
@@ -1124,11 +1151,11 @@ bidirectional pipes:
        git fast-import >fast-import-output
 ====
 
-A frontend set up this way can use `progress`, `ls`, and `cat-blob`
-commands to read information from the import in progress.
+A frontend set up this way can use `progress`, `get-mark`, `ls`, and
+`cat-blob` commands to read information from the import in progress.
 
 To avoid deadlock, such frontends must completely consume any
-pending output from `progress`, `ls`, and `cat-blob` before
+pending output from `progress`, `ls`, `get-mark`, and `cat-blob` before
 performing writes to fast-import that might block.
 
 Crash Reports
index 25c431d..84ee92e 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
         [--[no-]full] [--strict] [--verbose] [--lost-found]
-        [--[no-]dangling] [--[no-]progress] [<object>*]
+        [--[no-]dangling] [--[no-]progress] [--connectivity-only] [<object>*]
 
 DESCRIPTION
 -----------
@@ -60,6 +60,11 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs
        object pools.  This is now default; you can turn it off
        with --no-full.
 
+--connectivity-only::
+       Check only the connectivity of tags, commits and tree objects. By
+       avoiding to unpack blobs, this speeds up the operation, at the
+       expense of missing corrupt objects or other problematic issues.
+
 --strict::
        Enable more strict checking, namely to catch a file mode
        recorded with g+w bit set, which was created by older
index 5692945..97b9993 100644 (file)
@@ -184,6 +184,12 @@ log.date::
        `--date` option.)  Defaults to "default", which means to write
        dates like `Sat May 8 19:35:34 2010 -0500`.
 
+log.follow::
+       If a single <path> is given to git log, it will act as
+       if the `--follow` option was also used.  This has the same
+       limitations as `--follow`, i.e. it cannot be used to follow
+       multiple files and does not work well on non-linear history.
+
 log.showRoot::
        If `false`, `git log` and related commands will not treat the
        initial commit as a big creation event.  Any root commits in
index 1d01baa..ca03954 100644 (file)
@@ -213,6 +213,15 @@ rebase.autoSquash::
 rebase.autoStash::
        If set to true enable '--autostash' option by default.
 
+rebase.missingCommitsCheck::
+       If set to "warn", print warnings about removed commits in
+       interactive mode. If set to "error", print the warnings and
+       stop the rebase. If set to "ignore", no checking is
+       done. "ignore" by default.
+
+rebase.instructionFormat::
+       Custom commit list format to use during an '--interactive' rebase.
+
 OPTIONS
 -------
 --onto <newbase>::
@@ -359,6 +368,10 @@ default is `--no-fork-point`, otherwise the default is `--fork-point`.
        Make a list of the commits which are about to be rebased.  Let the
        user edit that list before rebasing.  This mode can also be used to
        split commits (see SPLITTING COMMITS below).
++
+The commit list format can be changed by setting the configuration option
+rebase.instructionFormat.  A customized instruction format will automatically
+have the long commit hash prepended to the format.
 
 -p::
 --preserve-merges::
@@ -514,6 +527,9 @@ rebasing.
 If you just want to edit the commit message for a commit, replace the
 command "pick" with the command "reword".
 
+To drop a commit, replace the command "pick" with "drop", or just
+delete the matching line.
+
 If you want to fold two or more commits into one, replace the command
 "pick" for the second and subsequent commits with "squash" or "fixup".
 If the commits had different authors, the folded commit will be
index 5e7908e..44c736f 100644 (file)
@@ -23,6 +23,7 @@ depending on the subcommand:
        [--dry-run] [--verbose] [--all | <refs>...]
 'git reflog delete' [--rewrite] [--updateref]
        [--dry-run] [--verbose] ref@\{specifier\}...
+'git reflog exists' <ref>
 
 Reference logs, or "reflogs", record when the tips of branches and
 other references were updated in the local repository. Reflogs are
@@ -52,6 +53,9 @@ argument must be an _exact_ entry (e.g. "`git reflog delete
 master@{2}`"). This subcommand is also typically not used directly by
 end users.
 
+The "exists" subcommand checks whether a ref has a reflog.  It exits
+with zero status if the reflog exists, and non-zero status if it does
+not.
 
 OPTIONS
 -------
index c483100..b6c6326 100644 (file)
@@ -311,8 +311,8 @@ Each line of options has this format:
 `<opt-spec>`::
        its format is the short option character, then the long option name
        separated by a comma. Both parts are not required, though at least one
-       is necessary. `h,help`, `dry-run` and `f` are all three correct
-       `<opt-spec>`.
+       is necessary. May not contain any of the `<flags>` characters.
+       `h,help`, `dry-run` and `f` are examples of correct `<opt-spec>`.
 
 `<flags>`::
        `<flags>` are of `*`, `=`, `?` or `!`.
index 7ae467b..f14705e 100644 (file)
@@ -49,17 +49,17 @@ Composing
        of 'sendemail.annotate'. See the CONFIGURATION section for
        'sendemail.multiEdit'.
 
---bcc=<address>::
+--bcc=<address>,...::
        Specify a "Bcc:" value for each email. Default is the value of
        'sendemail.bcc'.
 +
-The --bcc option must be repeated for each user you want on the bcc list.
+This option may be specified multiple times.
 
---cc=<address>::
+--cc=<address>,...::
        Specify a starting "Cc:" value for each email.
        Default is the value of 'sendemail.cc'.
 +
-The --cc option must be repeated for each user you want on the cc list.
+This option may be specified multiple times.
 
 --compose::
        Invoke a text editor (see GIT_EDITOR in linkgit:git-var[1])
@@ -110,13 +110,13 @@ is not set, this will be prompted for.
        Only necessary if --compose is also set.  If --compose
        is not set, this will be prompted for.
 
---to=<address>::
+--to=<address>,...::
        Specify the primary recipient of the emails generated. Generally, this
        will be the upstream maintainer of the project involved. Default is the
        value of the 'sendemail.to' configuration value; if that is unspecified,
        and --to-cmd is not specified, this will be prompted for.
 +
-The --to option must be repeated for each user you want on the to list.
+This option may be specified multiple times.
 
 --8bit-encoding=<encoding>::
        When encountering a non-ASCII message or subject that does not
index 034d10d..84f6496 100644 (file)
@@ -13,8 +13,7 @@ SYNOPSIS
        <tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
-       [--column[=<options>] | --no-column] [<pattern>...]
-       [<pattern>...]
+       [--column[=<options>] | --no-column] [--create-reflog] [<pattern>...]
 'git tag' -v <tagname>...
 
 DESCRIPTION
@@ -143,6 +142,9 @@ This option is only applicable when listing tags without annotation lines.
        all, 'whitespace' removes just leading/trailing whitespace lines and
        'strip' removes both whitespace and commentary.
 
+--create-reflog::
+       Create a reflog for the tag.
+
 <tagname>::
        The name of the tag to create, delete, or describe.
        The new tag name must pass all checks defined by
index 78a0d95..2f4ff50 100644 (file)
-A short Git tools survey
-========================
+Git Tools
+=========
 
+When Git was young, people looking for third-party Git-related tools came
+to the Git project itself to find them, thus a list of such tools was
+maintained here. These days, however, search engines fill that role much
+more efficiently, so this manually-maintained list has been retired.
 
-Introduction
-------------
-
-Apart from Git contrib/ area there are some others third-party tools
-you may want to look.
-
-This document presents a brief summary of each tool and the corresponding
-link.
-
-
-Alternative/Augmentative Porcelains
------------------------------------
-
-   - *Cogito* (http://www.kernel.org/pub/software/scm/cogito/)
-
-   Cogito is a version control system layered on top of the Git tree history
-   storage system. It aims at seamless user interface and ease of use,
-   providing generally smoother user experience than the "raw" Core Git
-   itself and indeed many other version control systems.
-
-   Cogito is no longer maintained as most of its functionality
-   is now in core Git.
-
-
-   - *pg* (http://www.spearce.org/category/projects/scm/pg/)
-
-   pg is a shell script wrapper around Git to help the user manage a set of
-   patches to files. pg is somewhat like quilt or StGit, but it does have a
-   slightly different feature set.
-
-
-   - *StGit* (http://www.procode.org/stgit/)
-
-   Stacked Git provides a quilt-like patch management functionality in the
-   Git environment. You can easily manage your patches in the scope of Git
-   until they get merged upstream.
-
-
-History Viewers
----------------
-
-   - *gitk* (shipped with git-core)
-
-   gitk is a simple Tk GUI for browsing history of Git repositories easily.
-
-
-   - *gitview*  (contrib/)
-
-   gitview is a GTK based repository browser for Git
-
-
-   - *gitweb* (shipped with git-core)
-
-   Gitweb provides full-fledged web interface for Git repositories.
-
-
-   - *qgit* (http://digilander.libero.it/mcostalba/)
-
-   QGit is a git/StGit GUI viewer built on Qt/C++. QGit could be used
-   to browse history and directory tree, view annotated files, commit
-   changes cherry picking single files or applying patches.
-   Currently it is the fastest and most feature rich among the Git
-   viewers and commit tools.
-
-   - *tig* (http://jonas.nitro.dk/tig/)
-
-   tig by Jonas Fonseca is a simple Git repository browser
-   written using ncurses. Basically, it just acts as a front-end
-   for git-log and git-show/git-diff. Additionally, you can also
-   use it as a pager for Git commands.
-
-
-Foreign SCM interface
----------------------
-
-   - *git-svn* (shipped with git-core)
-
-   git-svn is a simple conduit for changesets between a single Subversion
-   branch and Git.
-
-
-   - *quilt2git / git2quilt* (http://home-tj.org/wiki/index.php/Misc)
-
-   These utilities convert patch series in a quilt repository and commit
-   series in Git back and forth.
-
-
-   - *hg-to-git* (contrib/)
-
-   hg-to-git converts a Mercurial repository into a Git one, and
-   preserves the full branch history in the process. hg-to-git can
-   also be used in an incremental way to keep the Git repository
-   in sync with the master Mercurial repository.
-
-
-Others
-------
-
-   - *(h)gct* (http://www.cyd.liu.se/users/~freku045/gct/)
-
-   Commit Tool or (h)gct is a GUI enabled commit tool for Git and
-   Mercurial (hg). It allows the user to view diffs, select which files
-   to committed (or ignored / reverted) write commit messages and
-   perform the commit itself.
-
-   - *git.el* (contrib/)
-
-   This is an Emacs interface for Git. The user interface is modelled on
-   pcl-cvs. It has been developed on Emacs 21 and will probably need some
-   tweaking to work on XEmacs.
-
-
-http://git.or.cz/gitwiki/InterfacesFrontendsAndTools has more
-comprehensive list.
+See also the `contrib/` area, and the Git wiki:
+http://git.or.cz/gitwiki/InterfacesFrontendsAndTools
index c8f5ae5..969bfab 100644 (file)
@@ -8,7 +8,7 @@ git-update-ref - Update the object name stored in a ref safely
 SYNOPSIS
 --------
 [verse]
-'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
+'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
 
 DESCRIPTION
 -----------
@@ -67,6 +67,9 @@ performs all modifications together.  Specify commands of the form:
        verify SP <ref> [SP <oldvalue>] LF
        option SP <opt> LF
 
+With `--create-reflog`, update-ref will create a reflog for each ref
+even if one would not ordinarily be created.
+
 Quote fields containing whitespace as if they were strings in C source
 code; i.e., surrounded by double-quotes and with backslash escapes.
 Use 40 "0" characters or the empty string to specify a zero value.  To
index 9413e28..ecf4da1 100644 (file)
@@ -16,6 +16,10 @@ Validates the gpg signature created by 'git commit -S'.
 
 OPTIONS
 -------
+--raw::
+       Print the raw gpg status output to standard error instead of the normal
+       human-readable output.
+
 -v::
 --verbose::
        Print the contents of the commit object before validating it.
index f88ba96..d590edc 100644 (file)
@@ -16,6 +16,10 @@ Validates the gpg signature created by 'git tag'.
 
 OPTIONS
 -------
+--raw::
+       Print the raw gpg status output to standard error instead of the normal
+       human-readable output.
+
 -v::
 --verbose::
        Print the contents of the tag object before validating it.
index 3387e2f..fb68156 100644 (file)
@@ -3,7 +3,7 @@ git-worktree(1)
 
 NAME
 ----
-git-worktree - Manage multiple worktrees
+git-worktree - Manage multiple working trees
 
 
 SYNOPSIS
@@ -15,7 +15,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Manage multiple worktrees attached to the same repository.
+Manage multiple working trees attached to the same repository.
 
 A git repository can support multiple working trees, allowing you to check
 out more than one branch at a time.  With `git worktree add` a new working
@@ -27,19 +27,19 @@ bare repository) and zero or more linked working trees.
 When you are done with a linked working tree you can simply delete it.
 The working tree's administrative files in the repository (see
 "DETAILS" below) will eventually be removed automatically (see
-`gc.pruneworktreesexpire` in linkgit::git-config[1]), or you can run
+`gc.worktreePruneExpire` in linkgit:git-config[1]), or you can run
 `git worktree prune` in the main or any linked working tree to
 clean up any stale administrative files.
 
-If you move a linked working directory to another file system, or
+If you move a linked working tree to another file system, or
 within a file system that does not support hard links, you need to run
-at least one git command inside the linked working directory
+at least one git command inside the linked working tree
 (e.g. `git status`) in order to update its administrative files in the
 repository so that they do not get automatically pruned.
 
 If a linked working tree is stored on a portable device or network share
 which is not always mounted, you can prevent its administrative files from
-being pruned by creating a file named 'lock' alongside the other
+being pruned by creating a file named 'locked' alongside the other
 administrative files, optionally containing a plain text reason that
 pruning should be suppressed. See section "DETAILS" for more information.
 
@@ -51,9 +51,9 @@ 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.
 +
-If `<branch>` is omitted and neither `-b` nor `-B` is used, then, as a
-convenience, a new branch based at HEAD is created automatically, as if
-`-b $(basename <path>)` was specified.
+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::
 
@@ -64,22 +64,22 @@ OPTIONS
 
 -f::
 --force::
-       By default, `add` refuses to create a new worktree when `<branch>`
-       is already checked out by another worktree. This option overrides
+       By default, `add` refuses to create a new working tree when `<branch>`
+       is already checked out by another working tree. This option overrides
        that safeguard.
 
 -b <new-branch>::
 -B <new-branch>::
        With `add`, create a new branch named `<new-branch>` starting at
-       `<branch>`, and check out `<new-branch>` into the new worktree.
+       `<branch>`, and check out `<new-branch>` into the new working tree.
        If `<branch>` is omitted, it defaults to HEAD.
        By default, `-b` refuses to create a new branch if it already
        exists. `-B` overrides this safeguard, resetting `<new-branch>` to
        `<branch>`.
 
 --detach::
-       With `add`, detach HEAD in the new worktree. See "DETACHED HEAD" in
-       linkgit:git-checkout[1].
+       With `add`, detach HEAD in the new working tree. See "DETACHED HEAD"
+       in linkgit:git-checkout[1].
 
 -n::
 --dry-run::
@@ -91,7 +91,7 @@ OPTIONS
        With `prune`, report all removals.
 
 --expire <time>::
-       With `prune`, only expire unused worktrees older than <time>.
+       With `prune`, only expire unused working trees older than <time>.
 
 DETAILS
 -------
@@ -124,7 +124,7 @@ thumb is do not make any assumption about whether a path belongs to
 $GIT_DIR or $GIT_COMMON_DIR when you need to directly access something
 inside $GIT_DIR. Use `git rev-parse --git-path` to get the final path.
 
-To prevent a $GIT_DIR/worktrees entry from from being pruned (which
+To prevent a $GIT_DIR/worktrees entry from being pruned (which
 can be useful in some situations, such as when the
 entry's working tree is stored on a portable device), add a file named
 'locked' to the entry's directory. The file contains the reason in
@@ -139,9 +139,9 @@ EXAMPLES
 You are in the middle of a refactoring session and your boss comes in and
 demands that you fix something immediately. You might typically use
 linkgit:git-stash[1] to store your changes away temporarily, however, your
-worktree is in such a state of disarray (with new, moved, and removed files,
-and other bits and pieces strewn around) that you don't want to risk
-disturbing any of it. Instead, you create a temporary linked worktree to
+working tree is in such a state of disarray (with new, moved, and removed
+files, and other bits and pieces strewn around) that you don't want to risk
+disturbing any of it. Instead, you create a temporary linked working tree to
 make the emergency fix, remove it when done, and then resume your earlier
 refactoring session.
 
@@ -164,12 +164,12 @@ checkouts of a superproject.
 git-worktree could provide more automation for tasks currently
 performed manually, such as:
 
-- `remove` to remove a linked worktree and its administrative files (and
-  warn if the worktree is dirty)
-- `mv` to move or rename a worktree and update its administrative files
-- `list` to list linked worktrees
+- `remove` to remove a linked working tree and its administrative files (and
+  warn if the working tree is dirty)
+- `mv` to move or rename a working tree and update its administrative files
+- `list` to list linked working trees
 - `lock` to prevent automatic pruning of administrative files (for instance,
-  for a worktree on a portable device)
+  for a working tree on a portable device)
 
 GIT
 ---
index f4cb5cb..2795340 100644 (file)
@@ -46,11 +46,12 @@ Documentation for older releases are available here:
 * link:v2.5.0/git.html[documentation for release 2.5]
 
 * release notes for
-  link:RelNotes/2.5.0.txt[2.5],
+  link:RelNotes/2.5.0.txt[2.5].
 
-* link:v2.4.7/git.html[documentation for release 2.4.7]
+* link:v2.4.8/git.html[documentation for release 2.4.8]
 
 * release notes for
+  link:RelNotes/2.4.8.txt[2.4.8],
   link:RelNotes/2.4.7.txt[2.4.7],
   link:RelNotes/2.4.6.txt[2.4.6],
   link:RelNotes/2.4.5.txt[2.4.5],
@@ -780,7 +781,7 @@ The Git Repository
 ~~~~~~~~~~~~~~~~~~
 These environment variables apply to 'all' core Git commands. Nb: it
 is worth noting that they may be used/overridden by SCMS sitting above
-Git so take care if using Cogito etc.
+Git so take care if using a foreign front-end.
 
 'GIT_INDEX_FILE'::
        This environment allows the specification of an alternate
@@ -1009,9 +1010,20 @@ Unsetting the variable, or setting it to empty, "0" or
        Enables trace messages for all packets coming in or out of a
        given program. This can help with debugging object negotiation
        or other protocol issues. Tracing is turned off at a packet
-       starting with "PACK".
+       starting with "PACK" (but see 'GIT_TRACE_PACKFILE' below).
        See 'GIT_TRACE' for available trace output options.
 
+'GIT_TRACE_PACKFILE'::
+       Enables tracing of packfiles sent or received by a
+       given program. Unlike other trace output, this trace is
+       verbatim: no headers, and no quoting of binary data. You almost
+       certainly want to direct into a file (e.g.,
+       `GIT_TRACE_PACKFILE=/tmp/my.pack`) rather than displaying it on
+       the terminal or mixing it with other trace output.
++
+Note that this is currently only implemented for the client side
+of clones and fetches.
+
 'GIT_TRACE_PERFORMANCE'::
        Enables performance related trace messages, e.g. total execution
        time of each Git command.
index 81fe586..e3b1de8 100644 (file)
@@ -527,6 +527,8 @@ patterns are available:
 
 - `fortran` suitable for source code in the Fortran language.
 
+- `fountain` suitable for Fountain documents.
+
 - `html` suitable for HTML/XHTML documents.
 
 - `java` suitable for source code in the Java language.
index 7173b38..577ee84 100644 (file)
@@ -251,25 +251,25 @@ modules::
        Contains the git-repositories of the submodules.
 
 worktrees::
-       Contains worktree specific information of linked
-       checkouts. Each subdirectory contains the worktree-related
-       part of a linked checkout. This directory is ignored if
-       $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/worktrees" will be
-       used instead.
+       Contains administrative data for linked
+       working trees. Each subdirectory contains the working tree-related
+       part of a linked working tree. This directory is ignored if
+       $GIT_COMMON_DIR is set, in which case
+       "$GIT_COMMON_DIR/worktrees" will be used instead.
 
 worktrees/<id>/gitdir::
        A text file containing the absolute path back to the .git file
        that points to here. This is used to check if the linked
        repository has been manually removed and there is no need to
-       keep this directory any more. mtime of this file should be
+       keep this directory any more. The mtime of this file should be
        updated every time the linked repository is accessed.
 
 worktrees/<id>/locked::
-       If this file exists, the linked repository may be on a
-       portable device and not available. It does not mean that the
-       linked repository is gone and `worktrees/<id>` could be
-       removed. The file's content contains a reason string on why
-       the repository is locked.
+       If this file exists, the linked working tree may be on a
+       portable device and not available. The presence of this file
+       prevents `worktrees/<id>` from being pruned either automatically
+       or manually by `git worktree prune`. The file may contain a string
+       explaining why the repository is locked.
 
 worktrees/<id>/link::
        If this file exists, it is a hard link to the linked .git
index e9a1d5d..2dd79db 100644 (file)
@@ -1,18 +1,31 @@
-At the core level, Git is character encoding agnostic.
-
- - The pathnames recorded in the index and in the tree objects
-   are treated as uninterpreted sequences of non-NUL bytes.
-   What readdir(2) returns are what are recorded and compared
-   with the data Git keeps track of, which in turn are expected
-   to be what lstat(2) and creat(2) accepts.  There is no such
-   thing as pathname encoding translation.
+Git is to some extent character encoding agnostic.
 
  - The contents of the blob objects are uninterpreted sequences
    of bytes.  There is no encoding translation at the core
    level.
 
- - The commit log messages are uninterpreted sequences of non-NUL
-   bytes.
+ - Path names are encoded in UTF-8 normalization form C. This
+   applies to tree objects, the index file, ref names, as well as
+   path names in command line arguments, environment variables
+   and config files (`.git/config` (see linkgit:git-config[1]),
+   linkgit:gitignore[5], linkgit:gitattributes[5] and
+   linkgit:gitmodules[5]).
++
+Note that Git at the core level treats path names simply as
+sequences of non-NUL bytes, there are no path name encoding
+conversions (except on Mac and Windows). Therefore, using
+non-ASCII path names will mostly work even on platforms and file
+systems that use legacy extended ASCII encodings. However,
+repositories created on such systems will not work properly on
+UTF-8-based systems (e.g. Linux, Mac, Windows) and vice versa.
+Additionally, many Git-based tools simply assume path names to
+be UTF-8 and will fail to display other encodings correctly.
+
+ - Commit log messages are typically encoded in UTF-8, but other
+   extended ASCII encodings are also supported. This includes
+   ISO-8859-x, CP125x and many others, but _not_ UTF-16/32,
+   EBCDIC and CJK multi-byte encodings (GBK, Shift-JIS, Big5,
+   EUC-x, CP9xx etc.).
 
 Although we encourage that the commit log messages are encoded
 in UTF-8, both the core and Git Porcelain are designed not to
index 77ac439..a9b808f 100644 (file)
@@ -727,6 +727,11 @@ format, often found in email messages.
 +
 `--date=raw` shows the date in the internal raw Git format `%s %z` format.
 +
+`--date=format:...` feeds the format `...` to your system `strftime`.
+Use `--date=format:%c` to show the date in your system locale's
+preferred format.  See the `strftime` manual for a complete list of
+format placeholders.
++
 `--date=default` shows timestamps in the original time zone
 (either committer's or author's).
 
index 1a79781..8076172 100644 (file)
@@ -46,6 +46,9 @@ Functions
        Format a string and push it onto the end of the array. This is a
        convenience wrapper combining `strbuf_addf` and `argv_array_push`.
 
+`argv_array_pushv`::
+       Push a null-terminated array of strings onto the end of the array.
+
 `argv_array_pop`::
        Remove the final element from the array. If there are no
        elements in the array, do nothing.
index 1f2db31..5f0757d 100644 (file)
@@ -168,6 +168,12 @@ There are some macros to easily define options:
        Introduce an option with integer argument.
        The integer is put into `int_var`.
 
+`OPT_MAGNITUDE(short, long, &unsigned_long_var, description)`::
+       Introduce an option with a size argument. The argument must be a
+       non-negative integer and may include a suffix of 'k', 'm' or 'g' to
+       scale the provided value by 1024, 1024^2 or 1024^3 respectively.
+       The scaled value is put into `unsigned_long_var`.
+
 `OPT_DATE(short, long, &int_var, description)`::
        Introduce an option with date argument, see `approxidate()`.
        The timestamp is put into `int_var`.
@@ -212,6 +218,19 @@ There are some macros to easily define options:
        Use it to hide deprecated options that are still to be recognized
        and ignored silently.
 
+`OPT_PASSTHRU(short, long, &char_var, arg_str, description, flags)`::
+       Introduce an option that will be reconstructed into a char* string,
+       which must be initialized to NULL. This is useful when you need to
+       pass the command-line option to another command. Any previous value
+       will be overwritten, so this should only be used for options where
+       the last one specified on the command line wins.
+
+`OPT_PASSTHRU_ARGV(short, long, &argv_array_var, arg_str, description, flags)`::
+       Introduce an option where all instances of it on the command-line will
+       be reconstructed into an argv_array. This is useful when you need to
+       pass the command-line option, which can be specified multiple times,
+       to another command.
+
 
 The last element of the array must be `OPT_END()`.
 
index b7093af..7392ff6 100644 (file)
@@ -275,7 +275,7 @@ Git index format
 
     - The directory name terminated by NUL.
 
-    - A number of untrached file/dir names terminated by NUL.
+    - A number of untracked file/dir names terminated by NUL.
 
 The remaining data of each directory block is grouped by type:
 
index cd96820..fc45283 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.5.0
+DEF_VER=v2.5.0.GIT
 
 LF='
 '
@@ -38,5 +38,3 @@ test "$VN" = "$VC" || {
        echo >&2 "GIT_VERSION = $VN"
        echo "GIT_VERSION = $VN" >$GVF
 }
-
-
index 8c3c724..e39ca6c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -467,7 +467,6 @@ TEST_PROGRAMS_NEED_X =
 # interactive shell sessions without exporting it.
 unexport CDPATH
 
-SCRIPT_SH += git-am.sh
 SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
@@ -475,7 +474,6 @@ SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
-SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-remote-testgit.sh
@@ -763,6 +761,7 @@ LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
+LIB_OBJS += ref-filter.o
 LIB_OBJS += remote.o
 LIB_OBJS += replace_object.o
 LIB_OBJS += rerere.o
@@ -813,6 +812,7 @@ LIB_OBJS += xdiff-interface.o
 LIB_OBJS += zlib.o
 
 BUILTIN_OBJS += builtin/add.o
+BUILTIN_OBJS += builtin/am.o
 BUILTIN_OBJS += builtin/annotate.o
 BUILTIN_OBJS += builtin/apply.o
 BUILTIN_OBJS += builtin/archive.o
@@ -878,6 +878,7 @@ BUILTIN_OBJS += builtin/pack-refs.o
 BUILTIN_OBJS += builtin/patch-id.o
 BUILTIN_OBJS += builtin/prune-packed.o
 BUILTIN_OBJS += builtin/prune.o
+BUILTIN_OBJS += builtin/pull.o
 BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/read-tree.o
 BUILTIN_OBJS += builtin/receive-pack.o
index 3295d66..84a20c8 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.5.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.6.0.txt
\ No newline at end of file
index 575bec2..4965686 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -96,6 +96,14 @@ void NORETURN die_resolve_conflict(const char *me)
        die("Exiting because of an unresolved conflict.");
 }
 
+void NORETURN die_conclude_merge(void)
+{
+       error(_("You have not concluded your merge (MERGE_HEAD exists)."));
+       if (advice_resolve_conflict)
+               advise(_("Please, commit your changes before you can merge."));
+       die(_("Exiting because of unfinished merge."));
+}
+
 void detach_advice(const char *new_name)
 {
        const char fmt[] =
index 5ecc6c1..b341a55 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -24,6 +24,7 @@ __attribute__((format (printf, 1, 2)))
 void advise(const char *advice, ...);
 int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
+void NORETURN die_conclude_merge(void);
 void detach_advice(const char *new_name);
 
 #endif /* ADVICE_H */
index d37c41d..01b0899 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "refs.h"
 #include "commit.h"
 #include "tree-walk.h"
 #include "attr.h"
@@ -33,7 +34,7 @@ static void format_subst(const struct commit *commit,
        char *to_free = NULL;
        struct strbuf fmt = STRBUF_INIT;
        struct pretty_print_context ctx = {0};
-       ctx.date_mode = DATE_NORMAL;
+       ctx.date_mode.type = DATE_NORMAL;
        ctx.abbrev = DEFAULT_ABBREV;
 
        if (src == buf->buf)
index 256741d..eaed477 100644 (file)
@@ -49,6 +49,12 @@ void argv_array_pushl(struct argv_array *array, ...)
        va_end(ap);
 }
 
+void argv_array_pushv(struct argv_array *array, const char **argv)
+{
+       for (; *argv; argv++)
+               argv_array_push(array, *argv);
+}
+
 void argv_array_pop(struct argv_array *array)
 {
        if (!array->argc)
index c65e6e8..a2fa0aa 100644 (file)
@@ -17,6 +17,7 @@ __attribute__((format (printf,2,3)))
 void argv_array_pushf(struct argv_array *, const char *fmt, ...);
 LAST_ARG_MUST_BE_NULL
 void argv_array_pushl(struct argv_array *, ...);
+void argv_array_pushv(struct argv_array *, const char **);
 void argv_array_pop(struct argv_array *);
 void argv_array_clear(struct argv_array *);
 
index 03d5cd9..857cf59 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -21,6 +21,9 @@ static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
 static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
 static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
 
+static const char *term_bad;
+static const char *term_good;
+
 /* Remember to update object flag allocation in object.h */
 #define COUNTED                (1u<<16)
 
@@ -403,15 +406,21 @@ struct commit_list *find_bisection(struct commit_list *list,
 static int register_ref(const char *refname, const struct object_id *oid,
                        int flags, void *cb_data)
 {
-       if (!strcmp(refname, "bad")) {
+       struct strbuf good_prefix = STRBUF_INIT;
+       strbuf_addstr(&good_prefix, term_good);
+       strbuf_addstr(&good_prefix, "-");
+
+       if (!strcmp(refname, term_bad)) {
                current_bad_oid = xmalloc(sizeof(*current_bad_oid));
                oidcpy(current_bad_oid, oid);
-       } else if (starts_with(refname, "good-")) {
+       } else if (starts_with(refname, good_prefix.buf)) {
                sha1_array_append(&good_revs, oid->hash);
        } else if (starts_with(refname, "skip-")) {
                sha1_array_append(&skipped_revs, oid->hash);
        }
 
+       strbuf_release(&good_prefix);
+
        return 0;
 }
 
@@ -634,7 +643,7 @@ static void exit_if_skipped_commits(struct commit_list *tried,
                return;
 
        printf("There are only 'skip'ped commits left to test.\n"
-              "The first bad commit could be any of:\n");
+              "The first %s commit could be any of:\n", term_bad);
        print_commit_list(tried, "%s\n", "%s\n");
        if (bad)
                printf("%s\n", oid_to_hex(bad));
@@ -732,18 +741,24 @@ static void handle_bad_merge_base(void)
        if (is_expected_rev(current_bad_oid)) {
                char *bad_hex = oid_to_hex(current_bad_oid);
                char *good_hex = join_sha1_array_hex(&good_revs, ' ');
-
-               fprintf(stderr, "The merge base %s is bad.\n"
-                       "This means the bug has been fixed "
-                       "between %s and [%s].\n",
-                       bad_hex, bad_hex, good_hex);
-
+               if (!strcmp(term_bad, "bad") && !strcmp(term_good, "good")) {
+                       fprintf(stderr, "The merge base %s is bad.\n"
+                               "This means the bug has been fixed "
+                               "between %s and [%s].\n",
+                               bad_hex, bad_hex, good_hex);
+               } else {
+                       fprintf(stderr, "The merge base %s is %s.\n"
+                               "This means the first '%s' commit is "
+                               "between %s and [%s].\n",
+                               bad_hex, term_bad, term_good, bad_hex, good_hex);
+               }
                exit(3);
        }
 
-       fprintf(stderr, "Some good revs are not ancestor of the bad rev.\n"
+       fprintf(stderr, "Some %s revs are not ancestor of the %s rev.\n"
                "git bisect cannot work properly in this case.\n"
-               "Maybe you mistake good and bad revs?\n");
+               "Maybe you mistook %s and %s revs?\n",
+               term_good, term_bad, term_good, term_bad);
        exit(1);
 }
 
@@ -755,10 +770,10 @@ static void handle_skipped_merge_base(const unsigned char *mb)
 
        warning("the merge base between %s and [%s] "
                "must be skipped.\n"
-               "So we cannot be sure the first bad commit is "
+               "So we cannot be sure the first %s commit is "
                "between %s and %s.\n"
                "We continue anyway.",
-               bad_hex, good_hex, mb_hex, bad_hex);
+               bad_hex, good_hex, term_bad, mb_hex, bad_hex);
        free(good_hex);
 }
 
@@ -839,7 +854,7 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
        int fd;
 
        if (!current_bad_oid)
-               die("a bad revision is needed");
+               die("a %s revision is needed", term_bad);
 
        /* Check if file BISECT_ANCESTORS_OK exists. */
        if (!stat(filename, &st) && S_ISREG(st.st_mode))
@@ -889,6 +904,36 @@ static void show_diff_tree(const char *prefix, struct commit *commit)
        log_tree_commit(&opt, commit);
 }
 
+/*
+ * The terms used for this bisect session are stored in BISECT_TERMS.
+ * We read them and store them to adapt the messages accordingly.
+ * Default is bad/good.
+ */
+void read_bisect_terms(const char **read_bad, const char **read_good)
+{
+       struct strbuf str = STRBUF_INIT;
+       const char *filename = git_path("BISECT_TERMS");
+       FILE *fp = fopen(filename, "r");
+
+       if (!fp) {
+               if (errno == ENOENT) {
+                       *read_bad = "bad";
+                       *read_good = "good";
+                       return;
+               } else {
+                       die("could not read file '%s': %s", filename,
+                               strerror(errno));
+               }
+       } else {
+               strbuf_getline(&str, fp, '\n');
+               *read_bad = strbuf_detach(&str, NULL);
+               strbuf_getline(&str, fp, '\n');
+               *read_good = strbuf_detach(&str, NULL);
+       }
+       strbuf_release(&str);
+       fclose(fp);
+}
+
 /*
  * We use the convention that exiting with an exit code 10 means that
  * the bisection process finished successfully.
@@ -905,6 +950,7 @@ int bisect_next_all(const char *prefix, int no_checkout)
        const unsigned char *bisect_rev;
        char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
 
+       read_bisect_terms(&term_bad, &term_good);
        if (read_bisect_refs())
                die("reading bisect refs failed");
 
@@ -926,8 +972,10 @@ int bisect_next_all(const char *prefix, int no_checkout)
                 */
                exit_if_skipped_commits(tried, NULL);
 
-               printf("%s was both good and bad\n",
-                      oid_to_hex(current_bad_oid));
+               printf("%s was both %s and %s\n",
+                      oid_to_hex(current_bad_oid),
+                      term_good,
+                      term_bad);
                exit(1);
        }
 
@@ -942,7 +990,8 @@ int bisect_next_all(const char *prefix, int no_checkout)
 
        if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
                exit_if_skipped_commits(tried, current_bad_oid);
-               printf("%s is the first bad commit\n", bisect_rev_hex);
+               printf("%s is the first %s commit\n", bisect_rev_hex,
+                       term_bad);
                show_diff_tree(prefix, revs.commits->item);
                /* This means the bisection process succeeded. */
                exit(10);
index 2a6c831..acd12ef 100644 (file)
--- a/bisect.h
+++ b/bisect.h
@@ -26,4 +26,6 @@ extern int bisect_next_all(const char *prefix, int no_checkout);
 
 extern int estimate_bisect_steps(int all);
 
+extern void read_bisect_terms(const char **bad, const char **good);
+
 #endif
index b002435..c85be07 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -310,3 +310,70 @@ void remove_branch_state(void)
        unlink(git_path("MERGE_MODE"));
        unlink(git_path("SQUASH_MSG"));
 }
+
+static void check_linked_checkout(const char *branch, const char *id)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct strbuf path = STRBUF_INIT;
+       struct strbuf gitdir = STRBUF_INIT;
+
+       /*
+        * $GIT_COMMON_DIR/HEAD is practically outside
+        * $GIT_DIR so resolve_ref_unsafe() won't work (it
+        * uses git_path). Parse the ref ourselves.
+        */
+       if (id)
+               strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
+       else
+               strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
+
+       if (!strbuf_readlink(&sb, path.buf, 0)) {
+               if (!starts_with(sb.buf, "refs/") ||
+                   check_refname_format(sb.buf, 0))
+                       goto done;
+       } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&
+           starts_with(sb.buf, "ref:")) {
+               strbuf_remove(&sb, 0, strlen("ref:"));
+               strbuf_trim(&sb);
+       } else
+               goto done;
+       if (strcmp(sb.buf, branch))
+               goto done;
+       if (id) {
+               strbuf_reset(&path);
+               strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
+               if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
+                       goto done;
+               strbuf_rtrim(&gitdir);
+       } else
+               strbuf_addstr(&gitdir, get_git_common_dir());
+       skip_prefix(branch, "refs/heads/", &branch);
+       strbuf_strip_suffix(&gitdir, ".git");
+       die(_("'%s' is already checked out at '%s'"), branch, gitdir.buf);
+done:
+       strbuf_release(&path);
+       strbuf_release(&sb);
+       strbuf_release(&gitdir);
+}
+
+void die_if_checked_out(const char *branch)
+{
+       struct strbuf path = STRBUF_INIT;
+       DIR *dir;
+       struct dirent *d;
+
+       check_linked_checkout(branch, NULL);
+
+       strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
+       dir = opendir(path.buf);
+       strbuf_release(&path);
+       if (!dir)
+               return;
+
+       while ((d = readdir(dir)) != NULL) {
+               if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+                       continue;
+               check_linked_checkout(branch, d->d_name);
+       }
+       closedir(dir);
+}
index 64173ab..58aa45f 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -52,4 +52,11 @@ extern void install_branch_config(int flag, const char *local, const char *origi
  */
 extern int read_branch_desc(struct strbuf *, const char *branch_name);
 
+/*
+ * Check if a branch is checked out in the main worktree or any linked
+ * worktree and die (with a message describing its checkout location) if
+ * it is.
+ */
+extern void die_if_checked_out(const char *branch);
+
 #endif
index 9e04f97..79aaf0a 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -30,6 +30,7 @@ extern int textconv_object(const char *path, unsigned mode, const unsigned char
 extern int is_builtin(const char *s);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
+extern int cmd_am(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
 extern int cmd_apply(int argc, const char **argv, const char *prefix);
 extern int cmd_archive(int argc, const char **argv, const char *prefix);
@@ -98,6 +99,7 @@ extern int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
 extern int cmd_patch_id(int argc, const char **argv, const char *prefix);
 extern int cmd_prune(int argc, const char **argv, const char *prefix);
 extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
+extern int cmd_pull(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
index 4bd98b7..b2a5c57 100644 (file)
@@ -375,7 +375,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        if (add_new_files) {
                int baselen;
-               struct pathspec empty_pathspec;
 
                /* Set up the default git porcelain excludes */
                memset(&dir, 0, sizeof(dir));
@@ -384,7 +383,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                        setup_standard_excludes(&dir);
                }
 
-               memset(&empty_pathspec, 0, sizeof(empty_pathspec));
                /* This picks up the paths that are not tracked */
                baselen = fill_directory(&dir, &pathspec);
                if (pathspec.nr)
diff --git a/builtin/am.c b/builtin/am.c
new file mode 100644 (file)
index 0000000..1399c8d
--- /dev/null
@@ -0,0 +1,2321 @@
+/*
+ * Builtin "git am"
+ *
+ * Based on git-am.sh by Junio C Hamano.
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "exec_cmd.h"
+#include "parse-options.h"
+#include "dir.h"
+#include "run-command.h"
+#include "quote.h"
+#include "lockfile.h"
+#include "cache-tree.h"
+#include "refs.h"
+#include "commit.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "unpack-trees.h"
+#include "branch.h"
+#include "sequencer.h"
+#include "revision.h"
+#include "merge-recursive.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "notes-utils.h"
+#include "rerere.h"
+#include "prompt.h"
+
+/**
+ * Returns 1 if the file is empty or does not exist, 0 otherwise.
+ */
+static int is_empty_file(const char *filename)
+{
+       struct stat st;
+
+       if (stat(filename, &st) < 0) {
+               if (errno == ENOENT)
+                       return 1;
+               die_errno(_("could not stat %s"), filename);
+       }
+
+       return !st.st_size;
+}
+
+/**
+ * Like strbuf_getline(), but treats both '\n' and "\r\n" as line terminators.
+ */
+static int strbuf_getline_crlf(struct strbuf *sb, FILE *fp)
+{
+       if (strbuf_getwholeline(sb, fp, '\n'))
+               return EOF;
+       if (sb->buf[sb->len - 1] == '\n') {
+               strbuf_setlen(sb, sb->len - 1);
+               if (sb->len > 0 && sb->buf[sb->len - 1] == '\r')
+                       strbuf_setlen(sb, sb->len - 1);
+       }
+       return 0;
+}
+
+/**
+ * Returns the length of the first line of msg.
+ */
+static int linelen(const char *msg)
+{
+       return strchrnul(msg, '\n') - msg;
+}
+
+/**
+ * Returns true if `str` consists of only whitespace, false otherwise.
+ */
+static int str_isspace(const char *str)
+{
+       for (; *str; str++)
+               if (!isspace(*str))
+                       return 0;
+
+       return 1;
+}
+
+enum patch_format {
+       PATCH_FORMAT_UNKNOWN = 0,
+       PATCH_FORMAT_MBOX,
+       PATCH_FORMAT_STGIT,
+       PATCH_FORMAT_STGIT_SERIES,
+       PATCH_FORMAT_HG
+};
+
+enum keep_type {
+       KEEP_FALSE = 0,
+       KEEP_TRUE,      /* pass -k flag to git-mailinfo */
+       KEEP_NON_PATCH  /* pass -b flag to git-mailinfo */
+};
+
+enum scissors_type {
+       SCISSORS_UNSET = -1,
+       SCISSORS_FALSE = 0,  /* pass --no-scissors to git-mailinfo */
+       SCISSORS_TRUE        /* pass --scissors to git-mailinfo */
+};
+
+struct am_state {
+       /* state directory path */
+       char *dir;
+
+       /* current and last patch numbers, 1-indexed */
+       int cur;
+       int last;
+
+       /* commit metadata and message */
+       char *author_name;
+       char *author_email;
+       char *author_date;
+       char *msg;
+       size_t msg_len;
+
+       /* when --rebasing, records the original commit the patch came from */
+       unsigned char orig_commit[GIT_SHA1_RAWSZ];
+
+       /* number of digits in patch filename */
+       int prec;
+
+       /* various operating modes and command line options */
+       int interactive;
+       int threeway;
+       int quiet;
+       int signoff;
+       int utf8;
+       int keep; /* enum keep_type */
+       int message_id;
+       int scissors; /* enum scissors_type */
+       struct argv_array git_apply_opts;
+       const char *resolvemsg;
+       int committer_date_is_author_date;
+       int ignore_date;
+       int allow_rerere_autoupdate;
+       const char *sign_commit;
+       int rebasing;
+};
+
+/**
+ * Initializes am_state with the default values. The state directory is set to
+ * dir.
+ */
+static void am_state_init(struct am_state *state, const char *dir)
+{
+       int gpgsign;
+
+       memset(state, 0, sizeof(*state));
+
+       assert(dir);
+       state->dir = xstrdup(dir);
+
+       state->prec = 4;
+
+       git_config_get_bool("am.threeway", &state->threeway);
+
+       state->utf8 = 1;
+
+       git_config_get_bool("am.messageid", &state->message_id);
+
+       state->scissors = SCISSORS_UNSET;
+
+       argv_array_init(&state->git_apply_opts);
+
+       if (!git_config_get_bool("commit.gpgsign", &gpgsign))
+               state->sign_commit = gpgsign ? "" : NULL;
+}
+
+/**
+ * Releases memory allocated by an am_state.
+ */
+static void am_state_release(struct am_state *state)
+{
+       free(state->dir);
+       free(state->author_name);
+       free(state->author_email);
+       free(state->author_date);
+       free(state->msg);
+       argv_array_clear(&state->git_apply_opts);
+}
+
+/**
+ * Returns path relative to the am_state directory.
+ */
+static inline const char *am_path(const struct am_state *state, const char *path)
+{
+       return mkpath("%s/%s", state->dir, path);
+}
+
+/**
+ * If state->quiet is false, calls fprintf(fp, fmt, ...), and appends a newline
+ * at the end.
+ */
+static void say(const struct am_state *state, FILE *fp, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       if (!state->quiet) {
+               vfprintf(fp, fmt, ap);
+               putc('\n', fp);
+       }
+       va_end(ap);
+}
+
+/**
+ * Returns 1 if there is an am session in progress, 0 otherwise.
+ */
+static int am_in_progress(const struct am_state *state)
+{
+       struct stat st;
+
+       if (lstat(state->dir, &st) < 0 || !S_ISDIR(st.st_mode))
+               return 0;
+       if (lstat(am_path(state, "last"), &st) || !S_ISREG(st.st_mode))
+               return 0;
+       if (lstat(am_path(state, "next"), &st) || !S_ISREG(st.st_mode))
+               return 0;
+       return 1;
+}
+
+/**
+ * Reads the contents of `file` in the `state` directory into `sb`. Returns the
+ * number of bytes read on success, -1 if the file does not exist. If `trim` is
+ * set, trailing whitespace will be removed.
+ */
+static int read_state_file(struct strbuf *sb, const struct am_state *state,
+                       const char *file, int trim)
+{
+       strbuf_reset(sb);
+
+       if (strbuf_read_file(sb, am_path(state, file), 0) >= 0) {
+               if (trim)
+                       strbuf_trim(sb);
+
+               return sb->len;
+       }
+
+       if (errno == ENOENT)
+               return -1;
+
+       die_errno(_("could not read '%s'"), am_path(state, file));
+}
+
+/**
+ * Reads a KEY=VALUE shell variable assignment from `fp`, returning the VALUE
+ * as a newly-allocated string. VALUE must be a quoted string, and the KEY must
+ * match `key`. Returns NULL on failure.
+ *
+ * This is used by read_author_script() to read the GIT_AUTHOR_* variables from
+ * the author-script.
+ */
+static char *read_shell_var(FILE *fp, const char *key)
+{
+       struct strbuf sb = STRBUF_INIT;
+       const char *str;
+
+       if (strbuf_getline(&sb, fp, '\n'))
+               goto fail;
+
+       if (!skip_prefix(sb.buf, key, &str))
+               goto fail;
+
+       if (!skip_prefix(str, "=", &str))
+               goto fail;
+
+       strbuf_remove(&sb, 0, str - sb.buf);
+
+       str = sq_dequote(sb.buf);
+       if (!str)
+               goto fail;
+
+       return strbuf_detach(&sb, NULL);
+
+fail:
+       strbuf_release(&sb);
+       return NULL;
+}
+
+/**
+ * Reads and parses the state directory's "author-script" file, and sets
+ * state->author_name, state->author_email and state->author_date accordingly.
+ * Returns 0 on success, -1 if the file could not be parsed.
+ *
+ * The author script is of the format:
+ *
+ *     GIT_AUTHOR_NAME='$author_name'
+ *     GIT_AUTHOR_EMAIL='$author_email'
+ *     GIT_AUTHOR_DATE='$author_date'
+ *
+ * where $author_name, $author_email and $author_date are quoted. We are strict
+ * with our parsing, as the file was meant to be eval'd in the old git-am.sh
+ * script, and thus if the file differs from what this function expects, it is
+ * better to bail out than to do something that the user does not expect.
+ */
+static int read_author_script(struct am_state *state)
+{
+       const char *filename = am_path(state, "author-script");
+       FILE *fp;
+
+       assert(!state->author_name);
+       assert(!state->author_email);
+       assert(!state->author_date);
+
+       fp = fopen(filename, "r");
+       if (!fp) {
+               if (errno == ENOENT)
+                       return 0;
+               die_errno(_("could not open '%s' for reading"), filename);
+       }
+
+       state->author_name = read_shell_var(fp, "GIT_AUTHOR_NAME");
+       if (!state->author_name) {
+               fclose(fp);
+               return -1;
+       }
+
+       state->author_email = read_shell_var(fp, "GIT_AUTHOR_EMAIL");
+       if (!state->author_email) {
+               fclose(fp);
+               return -1;
+       }
+
+       state->author_date = read_shell_var(fp, "GIT_AUTHOR_DATE");
+       if (!state->author_date) {
+               fclose(fp);
+               return -1;
+       }
+
+       if (fgetc(fp) != EOF) {
+               fclose(fp);
+               return -1;
+       }
+
+       fclose(fp);
+       return 0;
+}
+
+/**
+ * Saves state->author_name, state->author_email and state->author_date in the
+ * state directory's "author-script" file.
+ */
+static void write_author_script(const struct am_state *state)
+{
+       struct strbuf sb = STRBUF_INIT;
+
+       strbuf_addstr(&sb, "GIT_AUTHOR_NAME=");
+       sq_quote_buf(&sb, state->author_name);
+       strbuf_addch(&sb, '\n');
+
+       strbuf_addstr(&sb, "GIT_AUTHOR_EMAIL=");
+       sq_quote_buf(&sb, state->author_email);
+       strbuf_addch(&sb, '\n');
+
+       strbuf_addstr(&sb, "GIT_AUTHOR_DATE=");
+       sq_quote_buf(&sb, state->author_date);
+       strbuf_addch(&sb, '\n');
+
+       write_file(am_path(state, "author-script"), 1, "%s", sb.buf);
+
+       strbuf_release(&sb);
+}
+
+/**
+ * Reads the commit message from the state directory's "final-commit" file,
+ * setting state->msg to its contents and state->msg_len to the length of its
+ * contents in bytes.
+ *
+ * Returns 0 on success, -1 if the file does not exist.
+ */
+static int read_commit_msg(struct am_state *state)
+{
+       struct strbuf sb = STRBUF_INIT;
+
+       assert(!state->msg);
+
+       if (read_state_file(&sb, state, "final-commit", 0) < 0) {
+               strbuf_release(&sb);
+               return -1;
+       }
+
+       state->msg = strbuf_detach(&sb, &state->msg_len);
+       return 0;
+}
+
+/**
+ * Saves state->msg in the state directory's "final-commit" file.
+ */
+static void write_commit_msg(const struct am_state *state)
+{
+       int fd;
+       const char *filename = am_path(state, "final-commit");
+
+       fd = xopen(filename, O_WRONLY | O_CREAT, 0666);
+       if (write_in_full(fd, state->msg, state->msg_len) < 0)
+               die_errno(_("could not write to %s"), filename);
+       close(fd);
+}
+
+/**
+ * Loads state from disk.
+ */
+static void am_load(struct am_state *state)
+{
+       struct strbuf sb = STRBUF_INIT;
+
+       if (read_state_file(&sb, state, "next", 1) < 0)
+               die("BUG: state file 'next' does not exist");
+       state->cur = strtol(sb.buf, NULL, 10);
+
+       if (read_state_file(&sb, state, "last", 1) < 0)
+               die("BUG: state file 'last' does not exist");
+       state->last = strtol(sb.buf, NULL, 10);
+
+       if (read_author_script(state) < 0)
+               die(_("could not parse author script"));
+
+       read_commit_msg(state);
+
+       if (read_state_file(&sb, state, "original-commit", 1) < 0)
+               hashclr(state->orig_commit);
+       else if (get_sha1_hex(sb.buf, state->orig_commit) < 0)
+               die(_("could not parse %s"), am_path(state, "original-commit"));
+
+       read_state_file(&sb, state, "threeway", 1);
+       state->threeway = !strcmp(sb.buf, "t");
+
+       read_state_file(&sb, state, "quiet", 1);
+       state->quiet = !strcmp(sb.buf, "t");
+
+       read_state_file(&sb, state, "sign", 1);
+       state->signoff = !strcmp(sb.buf, "t");
+
+       read_state_file(&sb, state, "utf8", 1);
+       state->utf8 = !strcmp(sb.buf, "t");
+
+       read_state_file(&sb, state, "keep", 1);
+       if (!strcmp(sb.buf, "t"))
+               state->keep = KEEP_TRUE;
+       else if (!strcmp(sb.buf, "b"))
+               state->keep = KEEP_NON_PATCH;
+       else
+               state->keep = KEEP_FALSE;
+
+       read_state_file(&sb, state, "messageid", 1);
+       state->message_id = !strcmp(sb.buf, "t");
+
+       read_state_file(&sb, state, "scissors", 1);
+       if (!strcmp(sb.buf, "t"))
+               state->scissors = SCISSORS_TRUE;
+       else if (!strcmp(sb.buf, "f"))
+               state->scissors = SCISSORS_FALSE;
+       else
+               state->scissors = SCISSORS_UNSET;
+
+       read_state_file(&sb, state, "apply-opt", 1);
+       argv_array_clear(&state->git_apply_opts);
+       if (sq_dequote_to_argv_array(sb.buf, &state->git_apply_opts) < 0)
+               die(_("could not parse %s"), am_path(state, "apply-opt"));
+
+       state->rebasing = !!file_exists(am_path(state, "rebasing"));
+
+       strbuf_release(&sb);
+}
+
+/**
+ * Removes the am_state directory, forcefully terminating the current am
+ * session.
+ */
+static void am_destroy(const struct am_state *state)
+{
+       struct strbuf sb = STRBUF_INIT;
+
+       strbuf_addstr(&sb, state->dir);
+       remove_dir_recursively(&sb, 0);
+       strbuf_release(&sb);
+}
+
+/**
+ * Runs applypatch-msg hook. Returns its exit code.
+ */
+static int run_applypatch_msg_hook(struct am_state *state)
+{
+       int ret;
+
+       assert(state->msg);
+       ret = run_hook_le(NULL, "applypatch-msg", am_path(state, "final-commit"), NULL);
+
+       if (!ret) {
+               free(state->msg);
+               state->msg = NULL;
+               if (read_commit_msg(state) < 0)
+                       die(_("'%s' was deleted by the applypatch-msg hook"),
+                               am_path(state, "final-commit"));
+       }
+
+       return ret;
+}
+
+/**
+ * Runs post-rewrite hook. Returns it exit code.
+ */
+static int run_post_rewrite_hook(const struct am_state *state)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       const char *hook = find_hook("post-rewrite");
+       int ret;
+
+       if (!hook)
+               return 0;
+
+       argv_array_push(&cp.args, hook);
+       argv_array_push(&cp.args, "rebase");
+
+       cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
+       cp.stdout_to_stderr = 1;
+
+       ret = run_command(&cp);
+
+       close(cp.in);
+       return ret;
+}
+
+/**
+ * Reads the state directory's "rewritten" file, and copies notes from the old
+ * commits listed in the file to their rewritten commits.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int copy_notes_for_rebase(const struct am_state *state)
+{
+       struct notes_rewrite_cfg *c;
+       struct strbuf sb = STRBUF_INIT;
+       const char *invalid_line = _("Malformed input line: '%s'.");
+       const char *msg = "Notes added by 'git rebase'";
+       FILE *fp;
+       int ret = 0;
+
+       assert(state->rebasing);
+
+       c = init_copy_notes_for_rewrite("rebase");
+       if (!c)
+               return 0;
+
+       fp = xfopen(am_path(state, "rewritten"), "r");
+
+       while (!strbuf_getline(&sb, fp, '\n')) {
+               unsigned char from_obj[GIT_SHA1_RAWSZ], to_obj[GIT_SHA1_RAWSZ];
+
+               if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) {
+                       ret = error(invalid_line, sb.buf);
+                       goto finish;
+               }
+
+               if (get_sha1_hex(sb.buf, from_obj)) {
+                       ret = error(invalid_line, sb.buf);
+                       goto finish;
+               }
+
+               if (sb.buf[GIT_SHA1_HEXSZ] != ' ') {
+                       ret = error(invalid_line, sb.buf);
+                       goto finish;
+               }
+
+               if (get_sha1_hex(sb.buf + GIT_SHA1_HEXSZ + 1, to_obj)) {
+                       ret = error(invalid_line, sb.buf);
+                       goto finish;
+               }
+
+               if (copy_note_for_rewrite(c, from_obj, to_obj))
+                       ret = error(_("Failed to copy notes from '%s' to '%s'"),
+                                       sha1_to_hex(from_obj), sha1_to_hex(to_obj));
+       }
+
+finish:
+       finish_copy_notes_for_rewrite(c, msg);
+       fclose(fp);
+       strbuf_release(&sb);
+       return ret;
+}
+
+/**
+ * Determines if the file looks like a piece of RFC2822 mail by grabbing all
+ * non-indented lines and checking if they look like they begin with valid
+ * header field names.
+ *
+ * Returns 1 if the file looks like a piece of mail, 0 otherwise.
+ */
+static int is_mail(FILE *fp)
+{
+       const char *header_regex = "^[!-9;-~]+:";
+       struct strbuf sb = STRBUF_INIT;
+       regex_t regex;
+       int ret = 1;
+
+       if (fseek(fp, 0L, SEEK_SET))
+               die_errno(_("fseek failed"));
+
+       if (regcomp(&regex, header_regex, REG_NOSUB | REG_EXTENDED))
+               die("invalid pattern: %s", header_regex);
+
+       while (!strbuf_getline_crlf(&sb, fp)) {
+               if (!sb.len)
+                       break; /* End of header */
+
+               /* Ignore indented folded lines */
+               if (*sb.buf == '\t' || *sb.buf == ' ')
+                       continue;
+
+               /* It's a header if it matches header_regex */
+               if (regexec(&regex, sb.buf, 0, NULL, 0)) {
+                       ret = 0;
+                       goto done;
+               }
+       }
+
+done:
+       regfree(&regex);
+       strbuf_release(&sb);
+       return ret;
+}
+
+/**
+ * Attempts to detect the patch_format of the patches contained in `paths`,
+ * returning the PATCH_FORMAT_* enum value. Returns PATCH_FORMAT_UNKNOWN if
+ * detection fails.
+ */
+static int detect_patch_format(const char **paths)
+{
+       enum patch_format ret = PATCH_FORMAT_UNKNOWN;
+       struct strbuf l1 = STRBUF_INIT;
+       struct strbuf l2 = STRBUF_INIT;
+       struct strbuf l3 = STRBUF_INIT;
+       FILE *fp;
+
+       /*
+        * We default to mbox format if input is from stdin and for directories
+        */
+       if (!*paths || !strcmp(*paths, "-") || is_directory(*paths))
+               return PATCH_FORMAT_MBOX;
+
+       /*
+        * Otherwise, check the first few lines of the first patch, starting
+        * from the first non-blank line, to try to detect its format.
+        */
+
+       fp = xfopen(*paths, "r");
+
+       while (!strbuf_getline_crlf(&l1, fp)) {
+               if (l1.len)
+                       break;
+       }
+
+       if (starts_with(l1.buf, "From ") || starts_with(l1.buf, "From: ")) {
+               ret = PATCH_FORMAT_MBOX;
+               goto done;
+       }
+
+       if (starts_with(l1.buf, "# This series applies on GIT commit")) {
+               ret = PATCH_FORMAT_STGIT_SERIES;
+               goto done;
+       }
+
+       if (!strcmp(l1.buf, "# HG changeset patch")) {
+               ret = PATCH_FORMAT_HG;
+               goto done;
+       }
+
+       strbuf_reset(&l2);
+       strbuf_getline_crlf(&l2, fp);
+       strbuf_reset(&l3);
+       strbuf_getline_crlf(&l3, fp);
+
+       /*
+        * If the second line is empty and the third is a From, Author or Date
+        * entry, this is likely an StGit patch.
+        */
+       if (l1.len && !l2.len &&
+               (starts_with(l3.buf, "From:") ||
+                starts_with(l3.buf, "Author:") ||
+                starts_with(l3.buf, "Date:"))) {
+               ret = PATCH_FORMAT_STGIT;
+               goto done;
+       }
+
+       if (l1.len && is_mail(fp)) {
+               ret = PATCH_FORMAT_MBOX;
+               goto done;
+       }
+
+done:
+       fclose(fp);
+       strbuf_release(&l1);
+       return ret;
+}
+
+/**
+ * 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)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf last = STRBUF_INIT;
+
+       cp.git_cmd = 1;
+       argv_array_push(&cp.args, "mailsplit");
+       argv_array_pushf(&cp.args, "-d%d", state->prec);
+       argv_array_pushf(&cp.args, "-o%s", state->dir);
+       argv_array_push(&cp.args, "-b");
+       if (keep_cr)
+               argv_array_push(&cp.args, "--keep-cr");
+       argv_array_push(&cp.args, "--");
+       argv_array_pushv(&cp.args, paths);
+
+       if (capture_command(&cp, &last, 8))
+               return -1;
+
+       state->cur = 1;
+       state->last = strtol(last.buf, NULL, 10);
+
+       return 0;
+}
+
+/**
+ * Callback signature for split_mail_conv(). The foreign patch should be
+ * read from `in`, and the converted patch (in RFC2822 mail format) should be
+ * written to `out`. Return 0 on success, or -1 on failure.
+ */
+typedef int (*mail_conv_fn)(FILE *out, FILE *in, int keep_cr);
+
+/**
+ * Calls `fn` for each file in `paths` to convert the foreign patch to the
+ * RFC2822 mail format suitable for parsing with git-mailinfo.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
+                       const char **paths, int keep_cr)
+{
+       static const char *stdin_only[] = {"-", NULL};
+       int i;
+
+       if (!*paths)
+               paths = stdin_only;
+
+       for (i = 0; *paths; paths++, i++) {
+               FILE *in, *out;
+               const char *mail;
+               int ret;
+
+               if (!strcmp(*paths, "-"))
+                       in = stdin;
+               else
+                       in = fopen(*paths, "r");
+
+               if (!in)
+                       return error(_("could not open '%s' for reading: %s"),
+                                       *paths, strerror(errno));
+
+               mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
+
+               out = fopen(mail, "w");
+               if (!out)
+                       return error(_("could not open '%s' for writing: %s"),
+                                       mail, strerror(errno));
+
+               ret = fn(out, in, keep_cr);
+
+               fclose(out);
+               fclose(in);
+
+               if (ret)
+                       return error(_("could not parse patch '%s'"), *paths);
+       }
+
+       state->cur = 1;
+       state->last = i;
+       return 0;
+}
+
+/**
+ * A split_mail_conv() callback that converts an StGit patch to an RFC2822
+ * message suitable for parsing with git-mailinfo.
+ */
+static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int subject_printed = 0;
+
+       while (!strbuf_getline(&sb, in, '\n')) {
+               const char *str;
+
+               if (str_isspace(sb.buf))
+                       continue;
+               else if (skip_prefix(sb.buf, "Author:", &str))
+                       fprintf(out, "From:%s\n", str);
+               else if (starts_with(sb.buf, "From") || starts_with(sb.buf, "Date"))
+                       fprintf(out, "%s\n", sb.buf);
+               else if (!subject_printed) {
+                       fprintf(out, "Subject: %s\n", sb.buf);
+                       subject_printed = 1;
+               } else {
+                       fprintf(out, "\n%s\n", sb.buf);
+                       break;
+               }
+       }
+
+       strbuf_reset(&sb);
+       while (strbuf_fread(&sb, 8192, in) > 0) {
+               fwrite(sb.buf, 1, sb.len, out);
+               strbuf_reset(&sb);
+       }
+
+       strbuf_release(&sb);
+       return 0;
+}
+
+/**
+ * This function only supports a single StGit series file in `paths`.
+ *
+ * Given an StGit series file, converts the StGit patches in the series into
+ * RFC2822 messages suitable for parsing with git-mailinfo, and queues them in
+ * the state directory.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail_stgit_series(struct am_state *state, const char **paths,
+                                       int keep_cr)
+{
+       const char *series_dir;
+       char *series_dir_buf;
+       FILE *fp;
+       struct argv_array patches = ARGV_ARRAY_INIT;
+       struct strbuf sb = STRBUF_INIT;
+       int ret;
+
+       if (!paths[0] || paths[1])
+               return error(_("Only one StGIT patch series can be applied at once"));
+
+       series_dir_buf = xstrdup(*paths);
+       series_dir = dirname(series_dir_buf);
+
+       fp = fopen(*paths, "r");
+       if (!fp)
+               return error(_("could not open '%s' for reading: %s"), *paths,
+                               strerror(errno));
+
+       while (!strbuf_getline(&sb, fp, '\n')) {
+               if (*sb.buf == '#')
+                       continue; /* skip comment lines */
+
+               argv_array_push(&patches, mkpath("%s/%s", series_dir, sb.buf));
+       }
+
+       fclose(fp);
+       strbuf_release(&sb);
+       free(series_dir_buf);
+
+       ret = split_mail_conv(stgit_patch_to_mail, state, patches.argv, keep_cr);
+
+       argv_array_clear(&patches);
+       return ret;
+}
+
+/**
+ * A split_patches_conv() callback that converts a mercurial patch to a RFC2822
+ * message suitable for parsing with git-mailinfo.
+ */
+static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+{
+       struct strbuf sb = STRBUF_INIT;
+
+       while (!strbuf_getline(&sb, in, '\n')) {
+               const char *str;
+
+               if (skip_prefix(sb.buf, "# User ", &str))
+                       fprintf(out, "From: %s\n", str);
+               else if (skip_prefix(sb.buf, "# Date ", &str)) {
+                       unsigned long timestamp;
+                       long tz, tz2;
+                       char *end;
+
+                       errno = 0;
+                       timestamp = strtoul(str, &end, 10);
+                       if (errno)
+                               return error(_("invalid timestamp"));
+
+                       if (!skip_prefix(end, " ", &str))
+                               return error(_("invalid Date line"));
+
+                       errno = 0;
+                       tz = strtol(str, &end, 10);
+                       if (errno)
+                               return error(_("invalid timezone offset"));
+
+                       if (*end)
+                               return error(_("invalid Date line"));
+
+                       /*
+                        * mercurial's timezone is in seconds west of UTC,
+                        * however git's timezone is in hours + minutes east of
+                        * UTC. Convert it.
+                        */
+                       tz2 = labs(tz) / 3600 * 100 + labs(tz) % 3600 / 60;
+                       if (tz > 0)
+                               tz2 = -tz2;
+
+                       fprintf(out, "Date: %s\n", show_date(timestamp, tz2, DATE_MODE(RFC2822)));
+               } else if (starts_with(sb.buf, "# ")) {
+                       continue;
+               } else {
+                       fprintf(out, "\n%s\n", sb.buf);
+                       break;
+               }
+       }
+
+       strbuf_reset(&sb);
+       while (strbuf_fread(&sb, 8192, in) > 0) {
+               fwrite(sb.buf, 1, sb.len, out);
+               strbuf_reset(&sb);
+       }
+
+       strbuf_release(&sb);
+       return 0;
+}
+
+/**
+ * Splits a list of files/directories into individual email patches. Each path
+ * in `paths` must be a file/directory that is formatted according to
+ * `patch_format`.
+ *
+ * Once split out, the individual email patches will be stored in the state
+ * directory, with each patch's filename being its index, padded to state->prec
+ * digits.
+ *
+ * state->cur will be set to the index of the first mail, and state->last will
+ * be set to the index of the last mail.
+ *
+ * Set keep_cr to 0 to convert all lines ending with \r\n to end with \n, 1
+ * to disable this behavior, -1 to use the default configured setting.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail(struct am_state *state, enum patch_format patch_format,
+                       const char **paths, int keep_cr)
+{
+       if (keep_cr < 0) {
+               keep_cr = 0;
+               git_config_get_bool("am.keepcr", &keep_cr);
+       }
+
+       switch (patch_format) {
+       case PATCH_FORMAT_MBOX:
+               return split_mail_mbox(state, paths, keep_cr);
+       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);
+       default:
+               die("BUG: invalid patch_format");
+       }
+       return -1;
+}
+
+/**
+ * Setup a new am session for applying patches
+ */
+static void am_setup(struct am_state *state, enum patch_format patch_format,
+                       const char **paths, int keep_cr)
+{
+       unsigned char curr_head[GIT_SHA1_RAWSZ];
+       const char *str;
+       struct strbuf sb = STRBUF_INIT;
+
+       if (!patch_format)
+               patch_format = detect_patch_format(paths);
+
+       if (!patch_format) {
+               fprintf_ln(stderr, _("Patch format detection failed."));
+               exit(128);
+       }
+
+       if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
+               die_errno(_("failed to create directory '%s'"), state->dir);
+
+       if (split_mail(state, patch_format, paths, keep_cr) < 0) {
+               am_destroy(state);
+               die(_("Failed to split patches."));
+       }
+
+       if (state->rebasing)
+               state->threeway = 1;
+
+       write_file(am_path(state, "threeway"), 1, state->threeway ? "t" : "f");
+
+       write_file(am_path(state, "quiet"), 1, state->quiet ? "t" : "f");
+
+       write_file(am_path(state, "sign"), 1, state->signoff ? "t" : "f");
+
+       write_file(am_path(state, "utf8"), 1, state->utf8 ? "t" : "f");
+
+       switch (state->keep) {
+       case KEEP_FALSE:
+               str = "f";
+               break;
+       case KEEP_TRUE:
+               str = "t";
+               break;
+       case KEEP_NON_PATCH:
+               str = "b";
+               break;
+       default:
+               die("BUG: invalid value for state->keep");
+       }
+
+       write_file(am_path(state, "keep"), 1, "%s", str);
+
+       write_file(am_path(state, "messageid"), 1, state->message_id ? "t" : "f");
+
+       switch (state->scissors) {
+       case SCISSORS_UNSET:
+               str = "";
+               break;
+       case SCISSORS_FALSE:
+               str = "f";
+               break;
+       case SCISSORS_TRUE:
+               str = "t";
+               break;
+       default:
+               die("BUG: invalid value for state->scissors");
+       }
+
+       write_file(am_path(state, "scissors"), 1, "%s", str);
+
+       sq_quote_argv(&sb, state->git_apply_opts.argv, 0);
+       write_file(am_path(state, "apply-opt"), 1, "%s", sb.buf);
+
+       if (state->rebasing)
+               write_file(am_path(state, "rebasing"), 1, "%s", "");
+       else
+               write_file(am_path(state, "applying"), 1, "%s", "");
+
+       if (!get_sha1("HEAD", curr_head)) {
+               write_file(am_path(state, "abort-safety"), 1, "%s", sha1_to_hex(curr_head));
+               if (!state->rebasing)
+                       update_ref("am", "ORIG_HEAD", curr_head, NULL, 0,
+                                       UPDATE_REFS_DIE_ON_ERR);
+       } else {
+               write_file(am_path(state, "abort-safety"), 1, "%s", "");
+               if (!state->rebasing)
+                       delete_ref("ORIG_HEAD", NULL, 0);
+       }
+
+       /*
+        * NOTE: Since the "next" and "last" files determine if an am_state
+        * session is in progress, they should be written last.
+        */
+
+       write_file(am_path(state, "next"), 1, "%d", state->cur);
+
+       write_file(am_path(state, "last"), 1, "%d", state->last);
+
+       strbuf_release(&sb);
+}
+
+/**
+ * Increments the patch pointer, and cleans am_state for the application of the
+ * next patch.
+ */
+static void am_next(struct am_state *state)
+{
+       unsigned char head[GIT_SHA1_RAWSZ];
+
+       free(state->author_name);
+       state->author_name = NULL;
+
+       free(state->author_email);
+       state->author_email = NULL;
+
+       free(state->author_date);
+       state->author_date = NULL;
+
+       free(state->msg);
+       state->msg = NULL;
+       state->msg_len = 0;
+
+       unlink(am_path(state, "author-script"));
+       unlink(am_path(state, "final-commit"));
+
+       hashclr(state->orig_commit);
+       unlink(am_path(state, "original-commit"));
+
+       if (!get_sha1("HEAD", head))
+               write_file(am_path(state, "abort-safety"), 1, "%s", sha1_to_hex(head));
+       else
+               write_file(am_path(state, "abort-safety"), 1, "%s", "");
+
+       state->cur++;
+       write_file(am_path(state, "next"), 1, "%d", state->cur);
+}
+
+/**
+ * Returns the filename of the current patch email.
+ */
+static const char *msgnum(const struct am_state *state)
+{
+       static struct strbuf sb = STRBUF_INIT;
+
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "%0*d", state->prec, state->cur);
+
+       return sb.buf;
+}
+
+/**
+ * Refresh and write index.
+ */
+static void refresh_and_write_cache(void)
+{
+       struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+       hold_locked_index(lock_file, 1);
+       refresh_cache(REFRESH_QUIET);
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+               die(_("unable to write index file"));
+}
+
+/**
+ * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
+ * branch, returns 1 if there are entries in the index, 0 otherwise. If an
+ * strbuf is provided, the space-separated list of files that differ will be
+ * appended to it.
+ */
+static int index_has_changes(struct strbuf *sb)
+{
+       unsigned char head[GIT_SHA1_RAWSZ];
+       int i;
+
+       if (!get_sha1_tree("HEAD", head)) {
+               struct diff_options opt;
+
+               diff_setup(&opt);
+               DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
+               if (!sb)
+                       DIFF_OPT_SET(&opt, QUICK);
+               do_diff_cache(head, &opt);
+               diffcore_std(&opt);
+               for (i = 0; sb && i < diff_queued_diff.nr; i++) {
+                       if (i)
+                               strbuf_addch(sb, ' ');
+                       strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
+               }
+               diff_flush(&opt);
+               return DIFF_OPT_TST(&opt, HAS_CHANGES) != 0;
+       } else {
+               for (i = 0; sb && i < active_nr; i++) {
+                       if (i)
+                               strbuf_addch(sb, ' ');
+                       strbuf_addstr(sb, active_cache[i]->name);
+               }
+               return !!active_nr;
+       }
+}
+
+/**
+ * Dies with a user-friendly message on how to proceed after resolving the
+ * problem. This message can be overridden with state->resolvemsg.
+ */
+static void NORETURN die_user_resolve(const struct am_state *state)
+{
+       if (state->resolvemsg) {
+               printf_ln("%s", state->resolvemsg);
+       } else {
+               const char *cmdline = state->interactive ? "git am -i" : "git am";
+
+               printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
+               printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+               printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+       }
+
+       exit(128);
+}
+
+/**
+ * Parses `mail` using git-mailinfo, extracting its patch and authorship info.
+ * state->msg will be set to the patch message. state->author_name,
+ * state->author_email and state->author_date will be set to the patch author's
+ * name, email and date respectively. The patch body will be written to the
+ * state directory's "patch" file.
+ *
+ * Returns 1 if the patch should be skipped, 0 otherwise.
+ */
+static int parse_mail(struct am_state *state, const char *mail)
+{
+       FILE *fp;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf sb = STRBUF_INIT;
+       struct strbuf msg = STRBUF_INIT;
+       struct strbuf author_name = STRBUF_INIT;
+       struct strbuf author_date = STRBUF_INIT;
+       struct strbuf author_email = STRBUF_INIT;
+       int ret = 0;
+
+       cp.git_cmd = 1;
+       cp.in = xopen(mail, O_RDONLY, 0);
+       cp.out = xopen(am_path(state, "info"), O_WRONLY | O_CREAT, 0777);
+
+       argv_array_push(&cp.args, "mailinfo");
+       argv_array_push(&cp.args, state->utf8 ? "-u" : "-n");
+
+       switch (state->keep) {
+       case KEEP_FALSE:
+               break;
+       case KEEP_TRUE:
+               argv_array_push(&cp.args, "-k");
+               break;
+       case KEEP_NON_PATCH:
+               argv_array_push(&cp.args, "-b");
+               break;
+       default:
+               die("BUG: invalid value for state->keep");
+       }
+
+       if (state->message_id)
+               argv_array_push(&cp.args, "-m");
+
+       switch (state->scissors) {
+       case SCISSORS_UNSET:
+               break;
+       case SCISSORS_FALSE:
+               argv_array_push(&cp.args, "--no-scissors");
+               break;
+       case SCISSORS_TRUE:
+               argv_array_push(&cp.args, "--scissors");
+               break;
+       default:
+               die("BUG: invalid value for state->scissors");
+       }
+
+       argv_array_push(&cp.args, am_path(state, "msg"));
+       argv_array_push(&cp.args, am_path(state, "patch"));
+
+       if (run_command(&cp) < 0)
+               die("could not parse patch");
+
+       close(cp.in);
+       close(cp.out);
+
+       /* Extract message and author information */
+       fp = xfopen(am_path(state, "info"), "r");
+       while (!strbuf_getline(&sb, fp, '\n')) {
+               const char *x;
+
+               if (skip_prefix(sb.buf, "Subject: ", &x)) {
+                       if (msg.len)
+                               strbuf_addch(&msg, '\n');
+                       strbuf_addstr(&msg, x);
+               } else if (skip_prefix(sb.buf, "Author: ", &x))
+                       strbuf_addstr(&author_name, x);
+               else if (skip_prefix(sb.buf, "Email: ", &x))
+                       strbuf_addstr(&author_email, x);
+               else if (skip_prefix(sb.buf, "Date: ", &x))
+                       strbuf_addstr(&author_date, x);
+       }
+       fclose(fp);
+
+       /* Skip pine's internal folder data */
+       if (!strcmp(author_name.buf, "Mail System Internal Data")) {
+               ret = 1;
+               goto finish;
+       }
+
+       if (is_empty_file(am_path(state, "patch"))) {
+               printf_ln(_("Patch is empty. Was it split wrong?"));
+               die_user_resolve(state);
+       }
+
+       strbuf_addstr(&msg, "\n\n");
+       if (strbuf_read_file(&msg, am_path(state, "msg"), 0) < 0)
+               die_errno(_("could not read '%s'"), am_path(state, "msg"));
+       stripspace(&msg, 0);
+
+       if (state->signoff)
+               append_signoff(&msg, 0, 0);
+
+       assert(!state->author_name);
+       state->author_name = strbuf_detach(&author_name, NULL);
+
+       assert(!state->author_email);
+       state->author_email = strbuf_detach(&author_email, NULL);
+
+       assert(!state->author_date);
+       state->author_date = strbuf_detach(&author_date, NULL);
+
+       assert(!state->msg);
+       state->msg = strbuf_detach(&msg, &state->msg_len);
+
+finish:
+       strbuf_release(&msg);
+       strbuf_release(&author_date);
+       strbuf_release(&author_email);
+       strbuf_release(&author_name);
+       strbuf_release(&sb);
+       return ret;
+}
+
+/**
+ * Sets commit_id to the commit hash where the mail was generated from.
+ * Returns 0 on success, -1 on failure.
+ */
+static int get_mail_commit_sha1(unsigned char *commit_id, const char *mail)
+{
+       struct strbuf sb = STRBUF_INIT;
+       FILE *fp = xfopen(mail, "r");
+       const char *x;
+
+       if (strbuf_getline(&sb, fp, '\n'))
+               return -1;
+
+       if (!skip_prefix(sb.buf, "From ", &x))
+               return -1;
+
+       if (get_sha1_hex(x, commit_id) < 0)
+               return -1;
+
+       strbuf_release(&sb);
+       fclose(fp);
+       return 0;
+}
+
+/**
+ * Sets state->msg, state->author_name, state->author_email, state->author_date
+ * to the commit's respective info.
+ */
+static void get_commit_info(struct am_state *state, struct commit *commit)
+{
+       const char *buffer, *ident_line, *author_date, *msg;
+       size_t ident_len;
+       struct ident_split ident_split;
+       struct strbuf sb = STRBUF_INIT;
+
+       buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
+
+       ident_line = find_commit_header(buffer, "author", &ident_len);
+
+       if (split_ident_line(&ident_split, ident_line, ident_len) < 0) {
+               strbuf_add(&sb, ident_line, ident_len);
+               die(_("invalid ident line: %s"), sb.buf);
+       }
+
+       assert(!state->author_name);
+       if (ident_split.name_begin) {
+               strbuf_add(&sb, ident_split.name_begin,
+                       ident_split.name_end - ident_split.name_begin);
+               state->author_name = strbuf_detach(&sb, NULL);
+       } else
+               state->author_name = xstrdup("");
+
+       assert(!state->author_email);
+       if (ident_split.mail_begin) {
+               strbuf_add(&sb, ident_split.mail_begin,
+                       ident_split.mail_end - ident_split.mail_begin);
+               state->author_email = strbuf_detach(&sb, NULL);
+       } else
+               state->author_email = xstrdup("");
+
+       author_date = show_ident_date(&ident_split, DATE_MODE(NORMAL));
+       strbuf_addstr(&sb, author_date);
+       assert(!state->author_date);
+       state->author_date = strbuf_detach(&sb, NULL);
+
+       assert(!state->msg);
+       msg = strstr(buffer, "\n\n");
+       if (!msg)
+               die(_("unable to parse commit %s"), sha1_to_hex(commit->object.sha1));
+       state->msg = xstrdup(msg + 2);
+       state->msg_len = strlen(state->msg);
+}
+
+/**
+ * Writes `commit` as a patch to the state directory's "patch" file.
+ */
+static void write_commit_patch(const struct am_state *state, struct commit *commit)
+{
+       struct rev_info rev_info;
+       FILE *fp;
+
+       fp = xfopen(am_path(state, "patch"), "w");
+       init_revisions(&rev_info, NULL);
+       rev_info.diff = 1;
+       rev_info.abbrev = 0;
+       rev_info.disable_stdin = 1;
+       rev_info.show_root_diff = 1;
+       rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
+       rev_info.no_commit_id = 1;
+       DIFF_OPT_SET(&rev_info.diffopt, BINARY);
+       DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);
+       rev_info.diffopt.use_color = 0;
+       rev_info.diffopt.file = fp;
+       rev_info.diffopt.close_file = 1;
+       add_pending_object(&rev_info, &commit->object, "");
+       diff_setup_done(&rev_info.diffopt);
+       log_tree_commit(&rev_info, commit);
+}
+
+/**
+ * Writes the diff of the index against HEAD as a patch to the state
+ * directory's "patch" file.
+ */
+static void write_index_patch(const struct am_state *state)
+{
+       struct tree *tree;
+       unsigned char head[GIT_SHA1_RAWSZ];
+       struct rev_info rev_info;
+       FILE *fp;
+
+       if (!get_sha1_tree("HEAD", head))
+               tree = lookup_tree(head);
+       else
+               tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
+
+       fp = xfopen(am_path(state, "patch"), "w");
+       init_revisions(&rev_info, NULL);
+       rev_info.diff = 1;
+       rev_info.disable_stdin = 1;
+       rev_info.no_commit_id = 1;
+       rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
+       rev_info.diffopt.use_color = 0;
+       rev_info.diffopt.file = fp;
+       rev_info.diffopt.close_file = 1;
+       add_pending_object(&rev_info, &tree->object, "");
+       diff_setup_done(&rev_info.diffopt);
+       run_diff_index(&rev_info, 1);
+}
+
+/**
+ * Like parse_mail(), but parses the mail by looking up its commit ID
+ * directly. This is used in --rebasing mode to bypass git-mailinfo's munging
+ * of patches.
+ *
+ * state->orig_commit will be set to the original commit ID.
+ *
+ * Will always return 0 as the patch should never be skipped.
+ */
+static int parse_mail_rebase(struct am_state *state, const char *mail)
+{
+       struct commit *commit;
+       unsigned char commit_sha1[GIT_SHA1_RAWSZ];
+
+       if (get_mail_commit_sha1(commit_sha1, mail) < 0)
+               die(_("could not parse %s"), mail);
+
+       commit = lookup_commit_or_die(commit_sha1, mail);
+
+       get_commit_info(state, commit);
+
+       write_commit_patch(state, commit);
+
+       hashcpy(state->orig_commit, commit_sha1);
+       write_file(am_path(state, "original-commit"), 1, "%s",
+                       sha1_to_hex(commit_sha1));
+
+       return 0;
+}
+
+/**
+ * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If
+ * `index_file` is not NULL, the patch will be applied to that index.
+ */
+static int run_apply(const struct am_state *state, const char *index_file)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       cp.git_cmd = 1;
+
+       if (index_file)
+               argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", index_file);
+
+       /*
+        * If we are allowed to fall back on 3-way merge, don't give false
+        * errors during the initial attempt.
+        */
+       if (state->threeway && !index_file) {
+               cp.no_stdout = 1;
+               cp.no_stderr = 1;
+       }
+
+       argv_array_push(&cp.args, "apply");
+
+       argv_array_pushv(&cp.args, state->git_apply_opts.argv);
+
+       if (index_file)
+               argv_array_push(&cp.args, "--cached");
+       else
+               argv_array_push(&cp.args, "--index");
+
+       argv_array_push(&cp.args, am_path(state, "patch"));
+
+       if (run_command(&cp))
+               return -1;
+
+       /* Reload index as git-apply will have modified it. */
+       discard_cache();
+       read_cache_from(index_file ? index_file : get_index_file());
+
+       return 0;
+}
+
+/**
+ * Builds an index that contains just the blobs needed for a 3way merge.
+ */
+static int build_fake_ancestor(const struct am_state *state, const char *index_file)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       cp.git_cmd = 1;
+       argv_array_push(&cp.args, "apply");
+       argv_array_pushv(&cp.args, state->git_apply_opts.argv);
+       argv_array_pushf(&cp.args, "--build-fake-ancestor=%s", index_file);
+       argv_array_push(&cp.args, am_path(state, "patch"));
+
+       if (run_command(&cp))
+               return -1;
+
+       return 0;
+}
+
+/**
+ * Attempt a threeway merge, using index_path as the temporary index.
+ */
+static int fall_back_threeway(const struct am_state *state, const char *index_path)
+{
+       unsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],
+                     our_tree[GIT_SHA1_RAWSZ];
+       const unsigned char *bases[1] = {orig_tree};
+       struct merge_options o;
+       struct commit *result;
+       char *his_tree_name;
+
+       if (get_sha1("HEAD", our_tree) < 0)
+               hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
+
+       if (build_fake_ancestor(state, index_path))
+               return error("could not build fake ancestor");
+
+       discard_cache();
+       read_cache_from(index_path);
+
+       if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))
+               return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
+
+       say(state, stdout, _("Using index info to reconstruct a base tree..."));
+
+       if (!state->quiet) {
+               /*
+                * List paths that needed 3-way fallback, so that the user can
+                * review them with extra care to spot mismerges.
+                */
+               struct rev_info rev_info;
+               const char *diff_filter_str = "--diff-filter=AM";
+
+               init_revisions(&rev_info, NULL);
+               rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
+               diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1);
+               add_pending_sha1(&rev_info, "HEAD", our_tree, 0);
+               diff_setup_done(&rev_info.diffopt);
+               run_diff_index(&rev_info, 1);
+       }
+
+       if (run_apply(state, index_path))
+               return error(_("Did you hand edit your patch?\n"
+                               "It does not apply to blobs recorded in its index."));
+
+       if (write_index_as_tree(his_tree, &the_index, index_path, 0, NULL))
+               return error("could not write tree");
+
+       say(state, stdout, _("Falling back to patching base and 3-way merge..."));
+
+       discard_cache();
+       read_cache();
+
+       /*
+        * This is not so wrong. Depending on which base we picked, orig_tree
+        * may be wildly different from ours, but his_tree has the same set of
+        * wildly different changes in parts the patch did not touch, so
+        * recursive ends up canceling them, saying that we reverted all those
+        * changes.
+        */
+
+       init_merge_options(&o);
+
+       o.branch1 = "HEAD";
+       his_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
+       o.branch2 = his_tree_name;
+
+       if (state->quiet)
+               o.verbosity = 0;
+
+       if (merge_recursive_generic(&o, our_tree, his_tree, 1, bases, &result)) {
+               rerere(state->allow_rerere_autoupdate);
+               free(his_tree_name);
+               return error(_("Failed to merge in the changes."));
+       }
+
+       free(his_tree_name);
+       return 0;
+}
+
+/**
+ * Commits the current index with state->msg as the commit message and
+ * state->author_name, state->author_email and state->author_date as the author
+ * information.
+ */
+static void do_commit(const struct am_state *state)
+{
+       unsigned char tree[GIT_SHA1_RAWSZ], parent[GIT_SHA1_RAWSZ],
+                     commit[GIT_SHA1_RAWSZ];
+       unsigned char *ptr;
+       struct commit_list *parents = NULL;
+       const char *reflog_msg, *author;
+       struct strbuf sb = STRBUF_INIT;
+
+       if (run_hook_le(NULL, "pre-applypatch", NULL))
+               exit(1);
+
+       if (write_cache_as_tree(tree, 0, NULL))
+               die(_("git write-tree failed to write a tree"));
+
+       if (!get_sha1_commit("HEAD", parent)) {
+               ptr = parent;
+               commit_list_insert(lookup_commit(parent), &parents);
+       } else {
+               ptr = NULL;
+               say(state, stderr, _("applying to an empty history"));
+       }
+
+       author = fmt_ident(state->author_name, state->author_email,
+                       state->ignore_date ? NULL : state->author_date,
+                       IDENT_STRICT);
+
+       if (state->committer_date_is_author_date)
+               setenv("GIT_COMMITTER_DATE",
+                       state->ignore_date ? "" : state->author_date, 1);
+
+       if (commit_tree(state->msg, state->msg_len, tree, parents, commit,
+                               author, state->sign_commit))
+               die(_("failed to write commit object"));
+
+       reflog_msg = getenv("GIT_REFLOG_ACTION");
+       if (!reflog_msg)
+               reflog_msg = "am";
+
+       strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
+                       state->msg);
+
+       update_ref(sb.buf, "HEAD", commit, ptr, 0, UPDATE_REFS_DIE_ON_ERR);
+
+       if (state->rebasing) {
+               FILE *fp = xfopen(am_path(state, "rewritten"), "a");
+
+               assert(!is_null_sha1(state->orig_commit));
+               fprintf(fp, "%s ", sha1_to_hex(state->orig_commit));
+               fprintf(fp, "%s\n", sha1_to_hex(commit));
+               fclose(fp);
+       }
+
+       run_hook_le(NULL, "post-applypatch", NULL);
+
+       strbuf_release(&sb);
+}
+
+/**
+ * Validates the am_state for resuming -- the "msg" and authorship fields must
+ * be filled up.
+ */
+static void validate_resume_state(const struct am_state *state)
+{
+       if (!state->msg)
+               die(_("cannot resume: %s does not exist."),
+                       am_path(state, "final-commit"));
+
+       if (!state->author_name || !state->author_email || !state->author_date)
+               die(_("cannot resume: %s does not exist."),
+                       am_path(state, "author-script"));
+}
+
+/**
+ * Interactively prompt the user on whether the current patch should be
+ * applied.
+ *
+ * Returns 0 if the user chooses to apply the patch, 1 if the user chooses to
+ * skip it.
+ */
+static int do_interactive(struct am_state *state)
+{
+       assert(state->msg);
+
+       if (!isatty(0))
+               die(_("cannot be interactive without stdin connected to a terminal."));
+
+       for (;;) {
+               const char *reply;
+
+               puts(_("Commit Body is:"));
+               puts("--------------------------");
+               printf("%s", state->msg);
+               puts("--------------------------");
+
+               /*
+                * TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+                * in your translation. The program will only accept English
+                * input at this point.
+                */
+               reply = git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);
+
+               if (!reply) {
+                       continue;
+               } else if (*reply == 'y' || *reply == 'Y') {
+                       return 0;
+               } else if (*reply == 'a' || *reply == 'A') {
+                       state->interactive = 0;
+                       return 0;
+               } else if (*reply == 'n' || *reply == 'N') {
+                       return 1;
+               } else if (*reply == 'e' || *reply == 'E') {
+                       struct strbuf msg = STRBUF_INIT;
+
+                       if (!launch_editor(am_path(state, "final-commit"), &msg, NULL)) {
+                               free(state->msg);
+                               state->msg = strbuf_detach(&msg, &state->msg_len);
+                       }
+                       strbuf_release(&msg);
+               } else if (*reply == 'v' || *reply == 'V') {
+                       const char *pager = git_pager(1);
+                       struct child_process cp = CHILD_PROCESS_INIT;
+
+                       if (!pager)
+                               pager = "cat";
+                       argv_array_push(&cp.args, pager);
+                       argv_array_push(&cp.args, am_path(state, "patch"));
+                       run_command(&cp);
+               }
+       }
+}
+
+/**
+ * Applies all queued mail.
+ *
+ * If `resume` is true, we are "resuming". The "msg" and authorship fields, as
+ * well as the state directory's "patch" file is used as-is for applying the
+ * patch and committing it.
+ */
+static void am_run(struct am_state *state, int resume)
+{
+       const char *argv_gc_auto[] = {"gc", "--auto", NULL};
+       struct strbuf sb = STRBUF_INIT;
+
+       unlink(am_path(state, "dirtyindex"));
+
+       refresh_and_write_cache();
+
+       if (index_has_changes(&sb)) {
+               write_file(am_path(state, "dirtyindex"), 1, "t");
+               die(_("Dirty index: cannot apply patches (dirty: %s)"), sb.buf);
+       }
+
+       strbuf_release(&sb);
+
+       while (state->cur <= state->last) {
+               const char *mail = am_path(state, msgnum(state));
+               int apply_status;
+
+               if (!file_exists(mail))
+                       goto next;
+
+               if (resume) {
+                       validate_resume_state(state);
+                       resume = 0;
+               } else {
+                       int skip;
+
+                       if (state->rebasing)
+                               skip = parse_mail_rebase(state, mail);
+                       else
+                               skip = parse_mail(state, mail);
+
+                       if (skip)
+                               goto next; /* mail should be skipped */
+
+                       write_author_script(state);
+                       write_commit_msg(state);
+               }
+
+               if (state->interactive && do_interactive(state))
+                       goto next;
+
+               if (run_applypatch_msg_hook(state))
+                       exit(1);
+
+               say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
+
+               apply_status = run_apply(state, NULL);
+
+               if (apply_status && state->threeway) {
+                       struct strbuf sb = STRBUF_INIT;
+
+                       strbuf_addstr(&sb, am_path(state, "patch-merge-index"));
+                       apply_status = fall_back_threeway(state, sb.buf);
+                       strbuf_release(&sb);
+
+                       /*
+                        * Applying the patch to an earlier tree and merging
+                        * the result may have produced the same tree as ours.
+                        */
+                       if (!apply_status && !index_has_changes(NULL)) {
+                               say(state, stdout, _("No changes -- Patch already applied."));
+                               goto next;
+                       }
+               }
+
+               if (apply_status) {
+                       int advice_amworkdir = 1;
+
+                       printf_ln(_("Patch failed at %s %.*s"), msgnum(state),
+                               linelen(state->msg), state->msg);
+
+                       git_config_get_bool("advice.amworkdir", &advice_amworkdir);
+
+                       if (advice_amworkdir)
+                               printf_ln(_("The copy of the patch that failed is found in: %s"),
+                                               am_path(state, "patch"));
+
+                       die_user_resolve(state);
+               }
+
+               do_commit(state);
+
+next:
+               am_next(state);
+       }
+
+       if (!is_empty_file(am_path(state, "rewritten"))) {
+               assert(state->rebasing);
+               copy_notes_for_rebase(state);
+               run_post_rewrite_hook(state);
+       }
+
+       /*
+        * In rebasing mode, it's up to the caller to take care of
+        * housekeeping.
+        */
+       if (!state->rebasing) {
+               am_destroy(state);
+               run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+       }
+}
+
+/**
+ * Resume the current am session after patch application failure. The user did
+ * all the hard work, and we do not have to do any patch application. Just
+ * trust and commit what the user has in the index and working tree.
+ */
+static void am_resolve(struct am_state *state)
+{
+       validate_resume_state(state);
+
+       say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
+
+       if (!index_has_changes(NULL)) {
+               printf_ln(_("No changes - did you forget to use 'git add'?\n"
+                       "If there is nothing left to stage, chances are that something else\n"
+                       "already introduced the same changes; you might want to skip this patch."));
+               die_user_resolve(state);
+       }
+
+       if (unmerged_cache()) {
+               printf_ln(_("You still have unmerged paths in your index.\n"
+                       "Did you forget to use 'git add'?"));
+               die_user_resolve(state);
+       }
+
+       if (state->interactive) {
+               write_index_patch(state);
+               if (do_interactive(state))
+                       goto next;
+       }
+
+       rerere(0);
+
+       do_commit(state);
+
+next:
+       am_next(state);
+       am_run(state, 0);
+}
+
+/**
+ * Performs a checkout fast-forward from `head` to `remote`. If `reset` is
+ * true, any unmerged entries will be discarded. Returns 0 on success, -1 on
+ * failure.
+ */
+static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
+{
+       struct lock_file *lock_file;
+       struct unpack_trees_options opts;
+       struct tree_desc t[2];
+
+       if (parse_tree(head) || parse_tree(remote))
+               return -1;
+
+       lock_file = xcalloc(1, sizeof(struct lock_file));
+       hold_locked_index(lock_file, 1);
+
+       refresh_cache(REFRESH_QUIET);
+
+       memset(&opts, 0, sizeof(opts));
+       opts.head_idx = 1;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
+       opts.update = 1;
+       opts.merge = 1;
+       opts.reset = reset;
+       opts.fn = twoway_merge;
+       init_tree_desc(&t[0], head->buffer, head->size);
+       init_tree_desc(&t[1], remote->buffer, remote->size);
+
+       if (unpack_trees(2, t, &opts)) {
+               rollback_lock_file(lock_file);
+               return -1;
+       }
+
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+               die(_("unable to write new index file"));
+
+       return 0;
+}
+
+/**
+ * Clean the index without touching entries that are not modified between
+ * `head` and `remote`.
+ */
+static int clean_index(const unsigned char *head, const unsigned char *remote)
+{
+       struct lock_file *lock_file;
+       struct tree *head_tree, *remote_tree, *index_tree;
+       unsigned char index[GIT_SHA1_RAWSZ];
+       struct pathspec pathspec;
+
+       head_tree = parse_tree_indirect(head);
+       if (!head_tree)
+               return error(_("Could not parse object '%s'."), sha1_to_hex(head));
+
+       remote_tree = parse_tree_indirect(remote);
+       if (!remote_tree)
+               return error(_("Could not parse object '%s'."), sha1_to_hex(remote));
+
+       read_cache_unmerged();
+
+       if (fast_forward_to(head_tree, head_tree, 1))
+               return -1;
+
+       if (write_cache_as_tree(index, 0, NULL))
+               return -1;
+
+       index_tree = parse_tree_indirect(index);
+       if (!index_tree)
+               return error(_("Could not parse object '%s'."), sha1_to_hex(index));
+
+       if (fast_forward_to(index_tree, remote_tree, 0))
+               return -1;
+
+       memset(&pathspec, 0, sizeof(pathspec));
+
+       lock_file = xcalloc(1, sizeof(struct lock_file));
+       hold_locked_index(lock_file, 1);
+
+       if (read_tree(remote_tree, 0, &pathspec)) {
+               rollback_lock_file(lock_file);
+               return -1;
+       }
+
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+               die(_("unable to write new index file"));
+
+       remove_branch_state();
+
+       return 0;
+}
+
+/**
+ * Resets rerere's merge resolution metadata.
+ */
+static void am_rerere_clear(void)
+{
+       struct string_list merge_rr = STRING_LIST_INIT_DUP;
+       int fd = setup_rerere(&merge_rr, 0);
+
+       if (fd < 0)
+               return;
+
+       rerere_clear(&merge_rr);
+       string_list_clear(&merge_rr, 1);
+}
+
+/**
+ * Resume the current am session by skipping the current patch.
+ */
+static void am_skip(struct am_state *state)
+{
+       unsigned char head[GIT_SHA1_RAWSZ];
+
+       am_rerere_clear();
+
+       if (get_sha1("HEAD", head))
+               hashcpy(head, EMPTY_TREE_SHA1_BIN);
+
+       if (clean_index(head, head))
+               die(_("failed to clean index"));
+
+       am_next(state);
+       am_run(state, 0);
+}
+
+/**
+ * Returns true if it is safe to reset HEAD to the ORIG_HEAD, false otherwise.
+ *
+ * It is not safe to reset HEAD when:
+ * 1. git-am previously failed because the index was dirty.
+ * 2. HEAD has moved since git-am previously failed.
+ */
+static int safe_to_abort(const struct am_state *state)
+{
+       struct strbuf sb = STRBUF_INIT;
+       unsigned char abort_safety[GIT_SHA1_RAWSZ], head[GIT_SHA1_RAWSZ];
+
+       if (file_exists(am_path(state, "dirtyindex")))
+               return 0;
+
+       if (read_state_file(&sb, state, "abort-safety", 1) > 0) {
+               if (get_sha1_hex(sb.buf, abort_safety))
+                       die(_("could not parse %s"), am_path(state, "abort_safety"));
+       } else
+               hashclr(abort_safety);
+
+       if (get_sha1("HEAD", head))
+               hashclr(head);
+
+       if (!hashcmp(head, abort_safety))
+               return 1;
+
+       error(_("You seem to have moved HEAD since the last 'am' failure.\n"
+               "Not rewinding to ORIG_HEAD"));
+
+       return 0;
+}
+
+/**
+ * Aborts the current am session if it is safe to do so.
+ */
+static void am_abort(struct am_state *state)
+{
+       unsigned char curr_head[GIT_SHA1_RAWSZ], orig_head[GIT_SHA1_RAWSZ];
+       int has_curr_head, has_orig_head;
+       char *curr_branch;
+
+       if (!safe_to_abort(state)) {
+               am_destroy(state);
+               return;
+       }
+
+       am_rerere_clear();
+
+       curr_branch = resolve_refdup("HEAD", 0, curr_head, NULL);
+       has_curr_head = !is_null_sha1(curr_head);
+       if (!has_curr_head)
+               hashcpy(curr_head, EMPTY_TREE_SHA1_BIN);
+
+       has_orig_head = !get_sha1("ORIG_HEAD", orig_head);
+       if (!has_orig_head)
+               hashcpy(orig_head, EMPTY_TREE_SHA1_BIN);
+
+       clean_index(curr_head, orig_head);
+
+       if (has_orig_head)
+               update_ref("am --abort", "HEAD", orig_head,
+                               has_curr_head ? curr_head : NULL, 0,
+                               UPDATE_REFS_DIE_ON_ERR);
+       else if (curr_branch)
+               delete_ref(curr_branch, NULL, REF_NODEREF);
+
+       free(curr_branch);
+       am_destroy(state);
+}
+
+/**
+ * parse_options() callback that validates and sets opt->value to the
+ * PATCH_FORMAT_* enum value corresponding to `arg`.
+ */
+static int parse_opt_patchformat(const struct option *opt, const char *arg, int unset)
+{
+       int *opt_value = opt->value;
+
+       if (!strcmp(arg, "mbox"))
+               *opt_value = PATCH_FORMAT_MBOX;
+       else if (!strcmp(arg, "stgit"))
+               *opt_value = PATCH_FORMAT_STGIT;
+       else if (!strcmp(arg, "stgit-series"))
+               *opt_value = PATCH_FORMAT_STGIT_SERIES;
+       else if (!strcmp(arg, "hg"))
+               *opt_value = PATCH_FORMAT_HG;
+       else
+               return error(_("Invalid value for --patch-format: %s"), arg);
+       return 0;
+}
+
+enum resume_mode {
+       RESUME_FALSE = 0,
+       RESUME_APPLY,
+       RESUME_RESOLVED,
+       RESUME_SKIP,
+       RESUME_ABORT
+};
+
+int cmd_am(int argc, const char **argv, const char *prefix)
+{
+       struct am_state state;
+       int binary = -1;
+       int keep_cr = -1;
+       int patch_format = PATCH_FORMAT_UNKNOWN;
+       enum resume_mode resume = RESUME_FALSE;
+
+       const char * const usage[] = {
+               N_("git am [options] [(<mbox>|<Maildir>)...]"),
+               N_("git am [options] (--continue | --skip | --abort)"),
+               NULL
+       };
+
+       struct option options[] = {
+               OPT_BOOL('i', "interactive", &state.interactive,
+                       N_("run interactively")),
+               OPT_HIDDEN_BOOL('b', "binary", &binary,
+                       N_("(historical option -- no-op")),
+               OPT_BOOL('3', "3way", &state.threeway,
+                       N_("allow fall back on 3way merging if needed")),
+               OPT__QUIET(&state.quiet, N_("be quiet")),
+               OPT_BOOL('s', "signoff", &state.signoff,
+                       N_("add a Signed-off-by line to the commit message")),
+               OPT_BOOL('u', "utf8", &state.utf8,
+                       N_("recode into utf8 (default)")),
+               OPT_SET_INT('k', "keep", &state.keep,
+                       N_("pass -k flag to git-mailinfo"), KEEP_TRUE),
+               OPT_SET_INT(0, "keep-non-patch", &state.keep,
+                       N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),
+               OPT_BOOL('m', "message-id", &state.message_id,
+                       N_("pass -m flag to git-mailinfo")),
+               { OPTION_SET_INT, 0, "keep-cr", &keep_cr, NULL,
+                 N_("pass --keep-cr flag to git-mailsplit for mbox format"),
+                 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1},
+               { OPTION_SET_INT, 0, "no-keep-cr", &keep_cr, NULL,
+                 N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),
+                 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0},
+               OPT_BOOL('c', "scissors", &state.scissors,
+                       N_("strip everything before a scissors line")),
+               OPT_PASSTHRU_ARGV(0, "whitespace", &state.git_apply_opts, N_("action"),
+                       N_("pass it through git-apply"),
+                       0),
+               OPT_PASSTHRU_ARGV(0, "ignore-space-change", &state.git_apply_opts, NULL,
+                       N_("pass it through git-apply"),
+                       PARSE_OPT_NOARG),
+               OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &state.git_apply_opts, NULL,
+                       N_("pass it through git-apply"),
+                       PARSE_OPT_NOARG),
+               OPT_PASSTHRU_ARGV(0, "directory", &state.git_apply_opts, N_("root"),
+                       N_("pass it through git-apply"),
+                       0),
+               OPT_PASSTHRU_ARGV(0, "exclude", &state.git_apply_opts, N_("path"),
+                       N_("pass it through git-apply"),
+                       0),
+               OPT_PASSTHRU_ARGV(0, "include", &state.git_apply_opts, N_("path"),
+                       N_("pass it through git-apply"),
+                       0),
+               OPT_PASSTHRU_ARGV('C', NULL, &state.git_apply_opts, N_("n"),
+                       N_("pass it through git-apply"),
+                       0),
+               OPT_PASSTHRU_ARGV('p', NULL, &state.git_apply_opts, N_("num"),
+                       N_("pass it through git-apply"),
+                       0),
+               OPT_CALLBACK(0, "patch-format", &patch_format, N_("format"),
+                       N_("format the patch(es) are in"),
+                       parse_opt_patchformat),
+               OPT_PASSTHRU_ARGV(0, "reject", &state.git_apply_opts, NULL,
+                       N_("pass it through git-apply"),
+                       PARSE_OPT_NOARG),
+               OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL,
+                       N_("override error message when patch failure occurs")),
+               OPT_CMDMODE(0, "continue", &resume,
+                       N_("continue applying patches after resolving a conflict"),
+                       RESUME_RESOLVED),
+               OPT_CMDMODE('r', "resolved", &resume,
+                       N_("synonyms for --continue"),
+                       RESUME_RESOLVED),
+               OPT_CMDMODE(0, "skip", &resume,
+                       N_("skip the current patch"),
+                       RESUME_SKIP),
+               OPT_CMDMODE(0, "abort", &resume,
+                       N_("restore the original branch and abort the patching operation."),
+                       RESUME_ABORT),
+               OPT_BOOL(0, "committer-date-is-author-date",
+                       &state.committer_date_is_author_date,
+                       N_("lie about committer date")),
+               OPT_BOOL(0, "ignore-date", &state.ignore_date,
+                       N_("use current timestamp for author date")),
+               OPT_RERERE_AUTOUPDATE(&state.allow_rerere_autoupdate),
+               { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
+                 N_("GPG-sign commits"),
+                 PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+               OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
+                       N_("(internal use for git-rebase)")),
+               OPT_END()
+       };
+
+       git_config(git_default_config, NULL);
+
+       am_state_init(&state, git_path("rebase-apply"));
+
+       argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+       if (binary >= 0)
+               fprintf_ln(stderr, _("The -b/--binary option has been a no-op for long time, and\n"
+                               "it will be removed. Please do not use it anymore."));
+
+       /* Ensure a valid committer ident can be constructed */
+       git_committer_info(IDENT_STRICT);
+
+       if (read_index_preload(&the_index, NULL) < 0)
+               die(_("failed to read the index"));
+
+       if (am_in_progress(&state)) {
+               /*
+                * Catch user error to feed us patches when there is a session
+                * in progress:
+                *
+                * 1. mbox path(s) are provided on the command-line.
+                * 2. stdin is not a tty: the user is trying to feed us a patch
+                *    from standard input. This is somewhat unreliable -- stdin
+                *    could be /dev/null for example and the caller did not
+                *    intend to feed us a patch but wanted to continue
+                *    unattended.
+                */
+               if (argc || (resume == RESUME_FALSE && !isatty(0)))
+                       die(_("previous rebase directory %s still exists but mbox given."),
+                               state.dir);
+
+               if (resume == RESUME_FALSE)
+                       resume = RESUME_APPLY;
+
+               am_load(&state);
+       } else {
+               struct argv_array paths = ARGV_ARRAY_INIT;
+               int i;
+
+               /*
+                * Handle stray state directory in the independent-run case. In
+                * the --rebasing case, it is up to the caller to take care of
+                * stray directories.
+                */
+               if (file_exists(state.dir) && !state.rebasing) {
+                       if (resume == RESUME_ABORT) {
+                               am_destroy(&state);
+                               am_state_release(&state);
+                               return 0;
+                       }
+
+                       die(_("Stray %s directory found.\n"
+                               "Use \"git am --abort\" to remove it."),
+                               state.dir);
+               }
+
+               if (resume)
+                       die(_("Resolve operation not in progress, we are not resuming."));
+
+               for (i = 0; i < argc; i++) {
+                       if (is_absolute_path(argv[i]) || !prefix)
+                               argv_array_push(&paths, argv[i]);
+                       else
+                               argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
+               }
+
+               am_setup(&state, patch_format, paths.argv, keep_cr);
+
+               argv_array_clear(&paths);
+       }
+
+       switch (resume) {
+       case RESUME_FALSE:
+               am_run(&state, 0);
+               break;
+       case RESUME_APPLY:
+               am_run(&state, 1);
+               break;
+       case RESUME_RESOLVED:
+               am_resolve(&state);
+               break;
+       case RESUME_SKIP:
+               am_skip(&state);
+               break;
+       case RESUME_ABORT:
+               am_abort(&state);
+               break;
+       default:
+               die("BUG: invalid resume value");
+       }
+
+       am_state_release(&state);
+
+       return 0;
+}
index a22ac17..04e4864 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "cache.h"
+#include "refs.h"
 #include "builtin.h"
 #include "blob.h"
 #include "commit.h"
@@ -50,7 +51,7 @@ static int xdl_opts;
 static int abbrev = -1;
 static int no_whole_file_rename;
 
-static enum date_mode blame_date_mode = DATE_ISO8601;
+static struct date_mode blame_date_mode = { DATE_ISO8601 };
 static size_t blame_date_width;
 
 static struct string_list mailmap;
@@ -1827,7 +1828,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
                size_t time_width;
                int tz;
                tz = atoi(tz_str);
-               time_str = show_date(time, tz, blame_date_mode);
+               time_str = show_date(time, tz, &blame_date_mode);
                strbuf_addstr(&time_buf, time_str);
                /*
                 * Add space paddings to time_buf to display a fixed width
@@ -2187,7 +2188,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "blame.date")) {
                if (!value)
                        return config_error_nonbool(var);
-               blame_date_mode = parse_date_format(value);
+               parse_date_format(value, &blame_date_mode);
                return 0;
        }
 
@@ -2569,13 +2570,13 @@ parse_done:
 
        if (cmd_is_annotate) {
                output_option |= OUTPUT_ANNOTATE_COMPAT;
-               blame_date_mode = DATE_ISO8601;
+               blame_date_mode.type = DATE_ISO8601;
        } else {
                blame_date_mode = revs.date_mode;
        }
 
        /* The maximum width used to show the dates */
-       switch (blame_date_mode) {
+       switch (blame_date_mode.type) {
        case DATE_RFC2822:
                blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
                break;
@@ -2604,6 +2605,9 @@ parse_done:
        case DATE_NORMAL:
                blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
                break;
+       case DATE_STRFTIME:
+               blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */
+               break;
        }
        blame_date_width -= 1; /* strip the null */
 
index b42e5b6..58aa84f 100644 (file)
@@ -160,7 +160,7 @@ static int branch_merged(int kind, const char *name,
 }
 
 static int check_branch_commit(const char *branchname, const char *refname,
-                              unsigned char *sha1, struct commit *head_rev,
+                              const unsigned char *sha1, struct commit *head_rev,
                               int kinds, int force)
 {
        struct commit *rev = lookup_commit_reference(sha1);
@@ -253,7 +253,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                        continue;
                }
 
-               if (delete_ref(name, sha1, REF_NODEREF)) {
+               if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
+                              REF_NODEREF)) {
                        error(remote_branch
                              ? _("Error deleting remote-tracking branch '%s'")
                              : _("Error deleting branch '%s'"),
index 049a95f..07baad1 100644 (file)
@@ -9,6 +9,16 @@
 #include "userdiff.h"
 #include "streaming.h"
 #include "tree-walk.h"
+#include "sha1-array.h"
+
+struct batch_options {
+       int enabled;
+       int follow_symlinks;
+       int print_contents;
+       int buffer_output;
+       int all_objects;
+       const char *format;
+};
 
 static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                        int unknown_type)
@@ -204,14 +214,25 @@ static size_t expand_format(struct strbuf *sb, const char *start, void *data)
        return end - start + 1;
 }
 
-static void print_object_or_die(int fd, struct expand_data *data)
+static void batch_write(struct batch_options *opt, const void *data, int len)
+{
+       if (opt->buffer_output) {
+               if (fwrite(data, 1, len, stdout) != len)
+                       die_errno("unable to write to stdout");
+       } else
+               write_or_die(1, data, len);
+}
+
+static void print_object_or_die(struct batch_options *opt, struct expand_data *data)
 {
        const unsigned char *sha1 = data->sha1;
 
        assert(data->info.typep);
 
        if (data->type == OBJ_BLOB) {
-               if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0)
+               if (opt->buffer_output)
+                       fflush(stdout);
+               if (stream_blob_to_fd(1, sha1, NULL, 0) < 0)
                        die("unable to stream %s to stdout", sha1_to_hex(sha1));
        }
        else {
@@ -227,29 +248,40 @@ static void print_object_or_die(int fd, struct expand_data *data)
                if (data->info.sizep && size != data->size)
                        die("object %s changed size!?", sha1_to_hex(sha1));
 
-               write_or_die(fd, contents, size);
+               batch_write(opt, contents, size);
                free(contents);
        }
 }
 
-struct batch_options {
-       int enabled;
-       int follow_symlinks;
-       int print_contents;
-       const char *format;
-};
-
-static int batch_one_object(const char *obj_name, struct batch_options *opt,
-                           struct expand_data *data)
+static void batch_object_write(const char *obj_name, struct batch_options *opt,
+                              struct expand_data *data)
 {
        struct strbuf buf = STRBUF_INIT;
+
+       if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
+               printf("%s missing\n", obj_name ? obj_name : sha1_to_hex(data->sha1));
+               fflush(stdout);
+               return;
+       }
+
+       strbuf_expand(&buf, opt->format, expand_format, data);
+       strbuf_addch(&buf, '\n');
+       batch_write(opt, buf.buf, buf.len);
+       strbuf_release(&buf);
+
+       if (opt->print_contents) {
+               print_object_or_die(opt, data);
+               batch_write(opt, "\n", 1);
+       }
+}
+
+static void batch_one_object(const char *obj_name, struct batch_options *opt,
+                            struct expand_data *data)
+{
        struct object_context ctx;
        int flags = opt->follow_symlinks ? GET_SHA1_FOLLOW_SYMLINKS : 0;
        enum follow_symlinks_result result;
 
-       if (!obj_name)
-          return 1;
-
        result = get_sha1_with_context(obj_name, flags, data->sha1, &ctx);
        if (result != FOUND) {
                switch (result) {
@@ -274,7 +306,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
                        break;
                }
                fflush(stdout);
-               return 0;
+               return;
        }
 
        if (ctx.mode == 0) {
@@ -282,24 +314,38 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
                       (uintmax_t)ctx.symlink_path.len,
                       ctx.symlink_path.buf);
                fflush(stdout);
-               return 0;
+               return;
        }
 
-       if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
-               printf("%s missing\n", obj_name);
-               fflush(stdout);
-               return 0;
-       }
+       batch_object_write(obj_name, opt, data);
+}
 
-       strbuf_expand(&buf, opt->format, expand_format, data);
-       strbuf_addch(&buf, '\n');
-       write_or_die(1, buf.buf, buf.len);
-       strbuf_release(&buf);
+struct object_cb_data {
+       struct batch_options *opt;
+       struct expand_data *expand;
+};
 
-       if (opt->print_contents) {
-               print_object_or_die(1, data);
-               write_or_die(1, "\n", 1);
-       }
+static void batch_object_cb(const unsigned char sha1[20], void *vdata)
+{
+       struct object_cb_data *data = vdata;
+       hashcpy(data->expand->sha1, sha1);
+       batch_object_write(NULL, data->opt, data->expand);
+}
+
+static int batch_loose_object(const unsigned char *sha1,
+                             const char *path,
+                             void *data)
+{
+       sha1_array_append(data, sha1);
+       return 0;
+}
+
+static int batch_packed_object(const unsigned char *sha1,
+                              struct packed_git *pack,
+                              uint32_t pos,
+                              void *data)
+{
+       sha1_array_append(data, sha1);
        return 0;
 }
 
@@ -330,6 +376,21 @@ static int batch_objects(struct batch_options *opt)
        if (opt->print_contents)
                data.info.typep = &data.type;
 
+       if (opt->all_objects) {
+               struct sha1_array sa = SHA1_ARRAY_INIT;
+               struct object_cb_data cb;
+
+               for_each_loose_object(batch_loose_object, &sa, 0);
+               for_each_packed_object(batch_packed_object, &sa, 0);
+
+               cb.opt = opt;
+               cb.expand = &data;
+               sha1_array_for_each_unique(&sa, batch_object_cb, &cb);
+
+               sha1_array_clear(&sa);
+               return 0;
+       }
+
        /*
         * We are going to call get_sha1 on a potentially very large number of
         * objects. In most large cases, these will be actual object sha1s. The
@@ -355,9 +416,7 @@ static int batch_objects(struct batch_options *opt)
                        data.rest = p;
                }
 
-               retval = batch_one_object(buf.buf, opt, &data);
-               if (retval)
-                       break;
+               batch_one_object(buf.buf, opt, &data);
        }
 
        strbuf_release(&buf);
@@ -412,8 +471,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                OPT_CMDMODE('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
                OPT_CMDMODE(0, "textconv", &opt,
                            N_("for blob objects, run textconv on object's content"), 'c'),
-               OPT_BOOL( 0, "allow-unknown-type", &unknown_type,
+               OPT_BOOL(0, "allow-unknown-type", &unknown_type,
                          N_("allow -s and -t to work with broken/corrupt objects")),
+               OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
                { OPTION_CALLBACK, 0, "batch", &batch, "format",
                        N_("show info and content of objects fed from the standard input"),
                        PARSE_OPT_OPTARG, batch_option_callback },
@@ -422,6 +482,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                        PARSE_OPT_OPTARG, batch_option_callback },
                OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks,
                         N_("follow in-tree symlinks (used with --batch or --batch-check)")),
+               OPT_BOOL(0, "batch-all-objects", &batch.all_objects,
+                        N_("show all objects with --batch or --batch-check")),
                OPT_END()
        };
 
@@ -446,7 +508,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                usage_with_options(cat_file_usage, options);
        }
 
-       if (batch.follow_symlinks && !batch.enabled) {
+       if ((batch.follow_symlinks || batch.all_objects) && !batch.enabled) {
                usage_with_options(cat_file_usage, options);
        }
 
index f71844a..e1403be 100644 (file)
@@ -48,8 +48,6 @@ struct checkout_opts {
        const char *prefix;
        struct pathspec pathspec;
        struct tree *source_tree;
-
-       int new_worktree_mode;
 };
 
 static int post_checkout_hook(struct commit *old, struct commit *new,
@@ -509,7 +507,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        topts.dir->flags |= DIR_SHOW_IGNORED;
                        setup_standard_excludes(topts.dir);
                }
-               tree = parse_tree_indirect(old->commit && !opts->new_worktree_mode ?
+               tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
                                           EMPTY_TREE_SHA1_BIN);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
@@ -612,22 +610,20 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
        if (opts->new_branch) {
                if (opts->new_orphan_branch) {
                        if (opts->new_branch_log && !log_all_ref_updates) {
-                               int temp;
-                               struct strbuf log_file = STRBUF_INIT;
                                int ret;
-                               const char *ref_name;
-
-                               ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
-                               temp = log_all_ref_updates;
-                               log_all_ref_updates = 1;
-                               ret = log_ref_setup(ref_name, &log_file);
-                               log_all_ref_updates = temp;
-                               strbuf_release(&log_file);
+                               char *refname;
+                               struct strbuf err = STRBUF_INIT;
+
+                               refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
+                               ret = safe_create_reflog(refname, 1, &err);
+                               free(refname);
                                if (ret) {
-                                       fprintf(stderr, _("Can not do reflog for '%s'\n"),
-                                           opts->new_orphan_branch);
+                                       fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
+                                               opts->new_orphan_branch, err.buf);
+                                       strbuf_release(&err);
                                        return;
                                }
+                               strbuf_release(&err);
                        }
                }
                else
@@ -832,8 +828,7 @@ static int switch_branches(const struct checkout_opts *opts,
                return ret;
        }
 
-       if (!opts->quiet && !old.path && old.commit &&
-           new->commit != old.commit && !opts->new_worktree_mode)
+       if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
                orphaned_commit_warning(old.commit, new->commit);
 
        update_refs_for_switch(opts, &old, new);
@@ -898,71 +893,6 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)
        return NULL;
 }
 
-static void check_linked_checkout(struct branch_info *new, const char *id)
-{
-       struct strbuf sb = STRBUF_INIT;
-       struct strbuf path = STRBUF_INIT;
-       struct strbuf gitdir = STRBUF_INIT;
-       const char *start, *end;
-
-       if (id)
-               strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
-       else
-               strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
-
-       if (strbuf_read_file(&sb, path.buf, 0) < 0 ||
-           !skip_prefix(sb.buf, "ref:", &start))
-               goto done;
-       while (isspace(*start))
-               start++;
-       end = start;
-       while (*end && !isspace(*end))
-               end++;
-       if (strncmp(start, new->path, end - start) || new->path[end - start] != '\0')
-               goto done;
-       if (id) {
-               strbuf_reset(&path);
-               strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
-               if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
-                       goto done;
-               strbuf_rtrim(&gitdir);
-       } else
-               strbuf_addstr(&gitdir, get_git_common_dir());
-       die(_("'%s' is already checked out at '%s'"), new->name, gitdir.buf);
-done:
-       strbuf_release(&path);
-       strbuf_release(&sb);
-       strbuf_release(&gitdir);
-}
-
-static void check_linked_checkouts(struct branch_info *new)
-{
-       struct strbuf path = STRBUF_INIT;
-       DIR *dir;
-       struct dirent *d;
-
-       strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
-       if ((dir = opendir(path.buf)) == NULL) {
-               strbuf_release(&path);
-               return;
-       }
-
-       /*
-        * $GIT_COMMON_DIR/HEAD is practically outside
-        * $GIT_DIR so resolve_ref_unsafe() won't work (it
-        * uses git_path). Parse the ref ourselves.
-        */
-       check_linked_checkout(new, NULL);
-
-       while ((d = readdir(dir)) != NULL) {
-               if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
-                       continue;
-               check_linked_checkout(new, d->d_name);
-       }
-       strbuf_release(&path);
-       closedir(dir);
-}
-
 static int parse_branchname_arg(int argc, const char **argv,
                                int dwim_new_local_branch_ok,
                                struct branch_info *new,
@@ -1170,14 +1100,14 @@ static int checkout_branch(struct checkout_opts *opts,
                die(_("Cannot switch branch to a non-commit '%s'"),
                    new->name);
 
-       if (new->path && !opts->force_detach && !opts->new_branch) {
+       if (new->path && !opts->force_detach && !opts->new_branch &&
+           !opts->ignore_other_worktrees) {
                unsigned char sha1[20];
                int flag;
                char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
                if (head_ref &&
-                   (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) &&
-                   !opts->ignore_other_worktrees)
-                       check_linked_checkouts(new);
+                   (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
+                       die_if_checked_out(new->path);
                free(head_ref);
        }
 
@@ -1241,8 +1171,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
-       opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL;
-
        if (conflict_style) {
                opts.merge = 1; /* implied */
                git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
index 6dcb72e..df53def 100644 (file)
@@ -10,7 +10,6 @@
 #include "cache.h"
 #include "dir.h"
 #include "parse-options.h"
-#include "refs.h"
 #include "string-list.h"
 #include "quote.h"
 #include "column.h"
@@ -148,6 +147,31 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+/*
+ * Return 1 if the given path is the root of a git repository or
+ * submodule else 0. Will not return 1 for bare repositories with the
+ * exception of creating a bare repository in "foo/.git" and calling
+ * is_git_repository("foo").
+ */
+static int is_git_repository(struct strbuf *path)
+{
+       int ret = 0;
+       int gitfile_error;
+       size_t orig_path_len = path->len;
+       assert(orig_path_len != 0);
+       if (path->buf[orig_path_len - 1] != '/')
+               strbuf_addch(path, '/');
+       strbuf_addstr(path, ".git");
+       if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf))
+               ret = 1;
+       if (gitfile_error == READ_GITFILE_ERR_OPEN_FAILED ||
+           gitfile_error == READ_GITFILE_ERR_READ_FAILED)
+               ret = 1;  /* This could be a real .git file, take the
+                          * safe option and avoid cleaning */
+       strbuf_setlen(path, orig_path_len);
+       return ret;
+}
+
 static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                int dry_run, int quiet, int *dir_gone)
 {
@@ -155,13 +179,11 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
        struct strbuf quoted = STRBUF_INIT;
        struct dirent *e;
        int res = 0, ret = 0, gone = 1, original_len = path->len, len;
-       unsigned char submodule_head[20];
        struct string_list dels = STRING_LIST_INIT_DUP;
 
        *dir_gone = 1;
 
-       if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
-                       !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
+       if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_git_repository(path)) {
                if (!quiet) {
                        quote_path_relative(path->buf, prefix, &quoted);
                        printf(dry_run ?  _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
index a72ff7e..303a3a7 100644 (file)
@@ -484,16 +484,26 @@ static void write_remote_refs(const struct ref *local_refs)
 {
        const struct ref *r;
 
-       lock_packed_refs(LOCK_DIE_ON_ERROR);
+       struct ref_transaction *t;
+       struct strbuf err = STRBUF_INIT;
+
+       t = ref_transaction_begin(&err);
+       if (!t)
+               die("%s", err.buf);
 
        for (r = local_refs; r; r = r->next) {
                if (!r->peer_ref)
                        continue;
-               add_packed_ref(r->peer_ref->name, r->old_sha1);
+               if (ref_transaction_create(t, r->peer_ref->name, r->old_sha1,
+                                          0, NULL, &err))
+                       die("%s", err.buf);
        }
 
-       if (commit_packed_refs())
-               die_errno("unable to overwrite old ref-pack file");
+       if (initial_ref_transaction_commit(t, &err))
+               die("%s", err.buf);
+
+       strbuf_release(&err);
+       ref_transaction_free(t);
 }
 
 static void write_followtags(const struct ref *refs, const char *msg)
index 254477f..7314f33 100644 (file)
@@ -856,7 +856,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                _("%s"
                                "Date:      %s"),
                                ident_shown++ ? "" : "\n",
-                               show_ident_date(&ai, DATE_NORMAL));
+                               show_ident_date(&ai, DATE_MODE(NORMAL)));
 
                if (!committer_ident_sufficiently_given())
                        status_printf_ln(s, GIT_COLOR_NORMAL,
@@ -1046,7 +1046,7 @@ static const char *find_author_by_nickname(const char *name)
        commit = get_revision(&revs);
        if (commit) {
                struct pretty_print_context ctx = {0};
-               ctx.date_mode = DATE_NORMAL;
+               ctx.date_mode.type = DATE_NORMAL;
                strbuf_release(&buf);
                format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
                clear_mailmap(&mailmap);
index b8182c2..d23f3be 100644 (file)
@@ -5,6 +5,7 @@
  */
 #include "builtin.h"
 #include "cache.h"
+#include "refs.h"
 #include "commit.h"
 #include "object.h"
 #include "tag.h"
index 8d5b2db..6d743aa 100644 (file)
@@ -790,20 +790,29 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
        if (4 < i && !strncmp(".git", url + i - 3, 4))
                url_len = i - 3;
 
-       for (ref = stale_refs; ref; ref = ref->next) {
-               if (!dry_run)
-                       result |= delete_ref(ref->name, NULL, 0);
-               if (verbosity >= 0 && !shown_url) {
-                       fprintf(stderr, _("From %.*s\n"), url_len, url);
-                       shown_url = 1;
-               }
-               if (verbosity >= 0) {
+       if (!dry_run) {
+               struct string_list refnames = STRING_LIST_INIT_NODUP;
+
+               for (ref = stale_refs; ref; ref = ref->next)
+                       string_list_append(&refnames, ref->name);
+
+               result = delete_refs(&refnames);
+               string_list_clear(&refnames, 0);
+       }
+
+       if (verbosity >= 0) {
+               for (ref = stale_refs; ref; ref = ref->next) {
+                       if (!shown_url) {
+                               fprintf(stderr, _("From %.*s\n"), url_len, url);
+                               shown_url = 1;
+                       }
                        fprintf(stderr, " x %-*s %-*s -> %s\n",
                                TRANSPORT_SUMMARY(_("[deleted]")),
                                REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
        }
+
        free(url);
        free_refs(stale_refs);
        return result;
@@ -979,17 +988,15 @@ static int get_remote_group(const char *key, const char *value, void *priv)
 {
        struct remote_group_data *g = priv;
 
-       if (starts_with(key, "remotes.") &&
-                       !strcmp(key + 8, g->name)) {
+       if (skip_prefix(key, "remotes.", &key) && !strcmp(key, g->name)) {
                /* split list by white space */
-               int space = strcspn(value, " \t\n");
                while (*value) {
-                       if (space > 1) {
+                       size_t wordlen = strcspn(value, " \t\n");
+
+                       if (wordlen >= 1)
                                string_list_append(g->list,
-                                                  xstrndup(value, space));
-                       }
-                       value += space + (value[space] != '\0');
-                       space = strcspn(value, " \t\n");
+                                                  xstrndup(value, wordlen));
+                       value += wordlen + (value[wordlen] != '\0');
                }
        }
 
index 05f4c26..4ba7f28 100644 (file)
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "refs.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
index cb7db23..7919206 100644 (file)
 #include "cache.h"
 #include "refs.h"
 #include "object.h"
-#include "tag.h"
-#include "commit.h"
-#include "tree.h"
-#include "blob.h"
-#include "quote.h"
 #include "parse-options.h"
-#include "remote.h"
-#include "color.h"
-
-/* Quoting styles */
-#define QUOTE_NONE 0
-#define QUOTE_SHELL 1
-#define QUOTE_PERL 2
-#define QUOTE_PYTHON 4
-#define QUOTE_TCL 8
-
-typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
-
-struct atom_value {
-       const char *s;
-       unsigned long ul; /* used for sorting when not FIELD_STR */
-};
-
-struct ref_sort {
-       struct ref_sort *next;
-       int atom; /* index into used_atom array */
-       unsigned reverse : 1;
-};
-
-struct refinfo {
-       char *refname;
-       unsigned char objectname[20];
-       int flag;
-       const char *symref;
-       struct atom_value *value;
-};
-
-static struct {
-       const char *name;
-       cmp_type cmp_type;
-} valid_atom[] = {
-       { "refname" },
-       { "objecttype" },
-       { "objectsize", FIELD_ULONG },
-       { "objectname" },
-       { "tree" },
-       { "parent" },
-       { "numparent", FIELD_ULONG },
-       { "object" },
-       { "type" },
-       { "tag" },
-       { "author" },
-       { "authorname" },
-       { "authoremail" },
-       { "authordate", FIELD_TIME },
-       { "committer" },
-       { "committername" },
-       { "committeremail" },
-       { "committerdate", FIELD_TIME },
-       { "tagger" },
-       { "taggername" },
-       { "taggeremail" },
-       { "taggerdate", FIELD_TIME },
-       { "creator" },
-       { "creatordate", FIELD_TIME },
-       { "subject" },
-       { "body" },
-       { "contents" },
-       { "contents:subject" },
-       { "contents:body" },
-       { "contents:signature" },
-       { "upstream" },
-       { "push" },
-       { "symref" },
-       { "flag" },
-       { "HEAD" },
-       { "color" },
-};
-
-/*
- * An atom is a valid field atom listed above, possibly prefixed with
- * a "*" to denote deref_tag().
- *
- * We parse given format string and sort specifiers, and make a list
- * of properties that we need to extract out of objects.  refinfo
- * structure will hold an array of values extracted that can be
- * indexed with the "atom number", which is an index into this
- * array.
- */
-static const char **used_atom;
-static cmp_type *used_atom_type;
-static int used_atom_cnt, need_tagged, need_symref;
-static int need_color_reset_at_eol;
-
-/*
- * Used to parse format string and sort specifiers
- */
-static int parse_atom(const char *atom, const char *ep)
-{
-       const char *sp;
-       int i, at;
-
-       sp = atom;
-       if (*sp == '*' && sp < ep)
-               sp++; /* deref */
-       if (ep <= sp)
-               die("malformed field name: %.*s", (int)(ep-atom), atom);
-
-       /* Do we have the atom already used elsewhere? */
-       for (i = 0; i < used_atom_cnt; i++) {
-               int len = strlen(used_atom[i]);
-               if (len == ep - atom && !memcmp(used_atom[i], atom, len))
-                       return i;
-       }
-
-       /* Is the atom a valid one? */
-       for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
-               int len = strlen(valid_atom[i].name);
-               /*
-                * If the atom name has a colon, strip it and everything after
-                * it off - it specifies the format for this entry, and
-                * shouldn't be used for checking against the valid_atom
-                * table.
-                */
-               const char *formatp = strchr(sp, ':');
-               if (!formatp || ep < formatp)
-                       formatp = ep;
-               if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
-                       break;
-       }
-
-       if (ARRAY_SIZE(valid_atom) <= i)
-               die("unknown field name: %.*s", (int)(ep-atom), atom);
-
-       /* Add it in, including the deref prefix */
-       at = used_atom_cnt;
-       used_atom_cnt++;
-       REALLOC_ARRAY(used_atom, used_atom_cnt);
-       REALLOC_ARRAY(used_atom_type, used_atom_cnt);
-       used_atom[at] = xmemdupz(atom, ep - atom);
-       used_atom_type[at] = valid_atom[i].cmp_type;
-       if (*atom == '*')
-               need_tagged = 1;
-       if (!strcmp(used_atom[at], "symref"))
-               need_symref = 1;
-       return at;
-}
-
-/*
- * In a format string, find the next occurrence of %(atom).
- */
-static const char *find_next(const char *cp)
-{
-       while (*cp) {
-               if (*cp == '%') {
-                       /*
-                        * %( is the start of an atom;
-                        * %% is a quoted per-cent.
-                        */
-                       if (cp[1] == '(')
-                               return cp;
-                       else if (cp[1] == '%')
-                               cp++; /* skip over two % */
-                       /* otherwise this is a singleton, literal % */
-               }
-               cp++;
-       }
-       return NULL;
-}
-
-/*
- * Make sure the format string is well formed, and parse out
- * the used atoms.
- */
-static int verify_format(const char *format)
-{
-       const char *cp, *sp;
-
-       need_color_reset_at_eol = 0;
-       for (cp = format; *cp && (sp = find_next(cp)); ) {
-               const char *color, *ep = strchr(sp, ')');
-               int at;
-
-               if (!ep)
-                       return error("malformed format string %s", sp);
-               /* sp points at "%(" and ep points at the closing ")" */
-               at = parse_atom(sp + 2, ep);
-               cp = ep + 1;
-
-               if (skip_prefix(used_atom[at], "color:", &color))
-                       need_color_reset_at_eol = !!strcmp(color, "reset");
-       }
-       return 0;
-}
-
-/*
- * Given an object name, read the object data and size, and return a
- * "struct object".  If the object data we are returning is also borrowed
- * by the "struct object" representation, set *eaten as well---it is a
- * signal from parse_object_buffer to us not to free the buffer.
- */
-static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)
-{
-       enum object_type type;
-       void *buf = read_sha1_file(sha1, &type, sz);
-
-       if (buf)
-               *obj = parse_object_buffer(sha1, type, *sz, buf, eaten);
-       else
-               *obj = NULL;
-       return buf;
-}
-
-static int grab_objectname(const char *name, const unsigned char *sha1,
-                           struct atom_value *v)
-{
-       if (!strcmp(name, "objectname")) {
-               char *s = xmalloc(41);
-               strcpy(s, sha1_to_hex(sha1));
-               v->s = s;
-               return 1;
-       }
-       if (!strcmp(name, "objectname:short")) {
-               v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
-               return 1;
-       }
-       return 0;
-}
-
-/* See grab_values */
-static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-       int i;
-
-       for (i = 0; i < used_atom_cnt; i++) {
-               const char *name = used_atom[i];
-               struct atom_value *v = &val[i];
-               if (!!deref != (*name == '*'))
-                       continue;
-               if (deref)
-                       name++;
-               if (!strcmp(name, "objecttype"))
-                       v->s = typename(obj->type);
-               else if (!strcmp(name, "objectsize")) {
-                       char *s = xmalloc(40);
-                       sprintf(s, "%lu", sz);
-                       v->ul = sz;
-                       v->s = s;
-               }
-               else if (deref)
-                       grab_objectname(name, obj->sha1, v);
-       }
-}
-
-/* See grab_values */
-static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-       int i;
-       struct tag *tag = (struct tag *) obj;
-
-       for (i = 0; i < used_atom_cnt; i++) {
-               const char *name = used_atom[i];
-               struct atom_value *v = &val[i];
-               if (!!deref != (*name == '*'))
-                       continue;
-               if (deref)
-                       name++;
-               if (!strcmp(name, "tag"))
-                       v->s = tag->tag;
-               else if (!strcmp(name, "type") && tag->tagged)
-                       v->s = typename(tag->tagged->type);
-               else if (!strcmp(name, "object") && tag->tagged) {
-                       char *s = xmalloc(41);
-                       strcpy(s, sha1_to_hex(tag->tagged->sha1));
-                       v->s = s;
-               }
-       }
-}
-
-/* See grab_values */
-static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-       int i;
-       struct commit *commit = (struct commit *) obj;
-
-       for (i = 0; i < used_atom_cnt; i++) {
-               const char *name = used_atom[i];
-               struct atom_value *v = &val[i];
-               if (!!deref != (*name == '*'))
-                       continue;
-               if (deref)
-                       name++;
-               if (!strcmp(name, "tree")) {
-                       char *s = xmalloc(41);
-                       strcpy(s, sha1_to_hex(commit->tree->object.sha1));
-                       v->s = s;
-               }
-               if (!strcmp(name, "numparent")) {
-                       char *s = xmalloc(40);
-                       v->ul = commit_list_count(commit->parents);
-                       sprintf(s, "%lu", v->ul);
-                       v->s = s;
-               }
-               else if (!strcmp(name, "parent")) {
-                       int num = commit_list_count(commit->parents);
-                       int i;
-                       struct commit_list *parents;
-                       char *s = xmalloc(41 * num + 1);
-                       v->s = s;
-                       for (i = 0, parents = commit->parents;
-                            parents;
-                            parents = parents->next, i = i + 41) {
-                               struct commit *parent = parents->item;
-                               strcpy(s+i, sha1_to_hex(parent->object.sha1));
-                               if (parents->next)
-                                       s[i+40] = ' ';
-                       }
-                       if (!i)
-                               *s = '\0';
-               }
-       }
-}
-
-static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)
-{
-       const char *eol;
-       while (*buf) {
-               if (!strncmp(buf, who, wholen) &&
-                   buf[wholen] == ' ')
-                       return buf + wholen + 1;
-               eol = strchr(buf, '\n');
-               if (!eol)
-                       return "";
-               eol++;
-               if (*eol == '\n')
-                       return ""; /* end of header */
-               buf = eol;
-       }
-       return "";
-}
-
-static const char *copy_line(const char *buf)
-{
-       const char *eol = strchrnul(buf, '\n');
-       return xmemdupz(buf, eol - buf);
-}
-
-static const char *copy_name(const char *buf)
-{
-       const char *cp;
-       for (cp = buf; *cp && *cp != '\n'; cp++) {
-               if (!strncmp(cp, " <", 2))
-                       return xmemdupz(buf, cp - buf);
-       }
-       return "";
-}
-
-static const char *copy_email(const char *buf)
-{
-       const char *email = strchr(buf, '<');
-       const char *eoemail;
-       if (!email)
-               return "";
-       eoemail = strchr(email, '>');
-       if (!eoemail)
-               return "";
-       return xmemdupz(email, eoemail + 1 - email);
-}
-
-static char *copy_subject(const char *buf, unsigned long len)
-{
-       char *r = xmemdupz(buf, len);
-       int i;
-
-       for (i = 0; i < len; i++)
-               if (r[i] == '\n')
-                       r[i] = ' ';
-
-       return r;
-}
-
-static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
-{
-       const char *eoemail = strstr(buf, "> ");
-       char *zone;
-       unsigned long timestamp;
-       long tz;
-       enum date_mode date_mode = DATE_NORMAL;
-       const char *formatp;
-
-       /*
-        * We got here because atomname ends in "date" or "date<something>";
-        * it's not possible that <something> is not ":<format>" because
-        * parse_atom() wouldn't have allowed it, so we can assume that no
-        * ":" means no format is specified, and use the default.
-        */
-       formatp = strchr(atomname, ':');
-       if (formatp != NULL) {
-               formatp++;
-               date_mode = parse_date_format(formatp);
-       }
-
-       if (!eoemail)
-               goto bad;
-       timestamp = strtoul(eoemail + 2, &zone, 10);
-       if (timestamp == ULONG_MAX)
-               goto bad;
-       tz = strtol(zone, NULL, 10);
-       if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
-               goto bad;
-       v->s = xstrdup(show_date(timestamp, tz, date_mode));
-       v->ul = timestamp;
-       return;
- bad:
-       v->s = "";
-       v->ul = 0;
-}
-
-/* See grab_values */
-static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-       int i;
-       int wholen = strlen(who);
-       const char *wholine = NULL;
-
-       for (i = 0; i < used_atom_cnt; i++) {
-               const char *name = used_atom[i];
-               struct atom_value *v = &val[i];
-               if (!!deref != (*name == '*'))
-                       continue;
-               if (deref)
-                       name++;
-               if (strncmp(who, name, wholen))
-                       continue;
-               if (name[wholen] != 0 &&
-                   strcmp(name + wholen, "name") &&
-                   strcmp(name + wholen, "email") &&
-                   !starts_with(name + wholen, "date"))
-                       continue;
-               if (!wholine)
-                       wholine = find_wholine(who, wholen, buf, sz);
-               if (!wholine)
-                       return; /* no point looking for it */
-               if (name[wholen] == 0)
-                       v->s = copy_line(wholine);
-               else if (!strcmp(name + wholen, "name"))
-                       v->s = copy_name(wholine);
-               else if (!strcmp(name + wholen, "email"))
-                       v->s = copy_email(wholine);
-               else if (starts_with(name + wholen, "date"))
-                       grab_date(wholine, v, name);
-       }
-
-       /*
-        * For a tag or a commit object, if "creator" or "creatordate" is
-        * requested, do something special.
-        */
-       if (strcmp(who, "tagger") && strcmp(who, "committer"))
-               return; /* "author" for commit object is not wanted */
-       if (!wholine)
-               wholine = find_wholine(who, wholen, buf, sz);
-       if (!wholine)
-               return;
-       for (i = 0; i < used_atom_cnt; i++) {
-               const char *name = used_atom[i];
-               struct atom_value *v = &val[i];
-               if (!!deref != (*name == '*'))
-                       continue;
-               if (deref)
-                       name++;
-
-               if (starts_with(name, "creatordate"))
-                       grab_date(wholine, v, name);
-               else if (!strcmp(name, "creator"))
-                       v->s = copy_line(wholine);
-       }
-}
-
-static void find_subpos(const char *buf, unsigned long sz,
-                       const char **sub, unsigned long *sublen,
-                       const char **body, unsigned long *bodylen,
-                       unsigned long *nonsiglen,
-                       const char **sig, unsigned long *siglen)
-{
-       const char *eol;
-       /* skip past header until we hit empty line */
-       while (*buf && *buf != '\n') {
-               eol = strchrnul(buf, '\n');
-               if (*eol)
-                       eol++;
-               buf = eol;
-       }
-       /* skip any empty lines */
-       while (*buf == '\n')
-               buf++;
-
-       /* parse signature first; we might not even have a subject line */
-       *sig = buf + parse_signature(buf, strlen(buf));
-       *siglen = strlen(*sig);
-
-       /* subject is first non-empty line */
-       *sub = buf;
-       /* subject goes to first empty line */
-       while (buf < *sig && *buf && *buf != '\n') {
-               eol = strchrnul(buf, '\n');
-               if (*eol)
-                       eol++;
-               buf = eol;
-       }
-       *sublen = buf - *sub;
-       /* drop trailing newline, if present */
-       if (*sublen && (*sub)[*sublen - 1] == '\n')
-               *sublen -= 1;
-
-       /* skip any empty lines */
-       while (*buf == '\n')
-               buf++;
-       *body = buf;
-       *bodylen = strlen(buf);
-       *nonsiglen = *sig - buf;
-}
-
-/* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-       int i;
-       const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
-       unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
-
-       for (i = 0; i < used_atom_cnt; i++) {
-               const char *name = used_atom[i];
-               struct atom_value *v = &val[i];
-               if (!!deref != (*name == '*'))
-                       continue;
-               if (deref)
-                       name++;
-               if (strcmp(name, "subject") &&
-                   strcmp(name, "body") &&
-                   strcmp(name, "contents") &&
-                   strcmp(name, "contents:subject") &&
-                   strcmp(name, "contents:body") &&
-                   strcmp(name, "contents:signature"))
-                       continue;
-               if (!subpos)
-                       find_subpos(buf, sz,
-                                   &subpos, &sublen,
-                                   &bodypos, &bodylen, &nonsiglen,
-                                   &sigpos, &siglen);
-
-               if (!strcmp(name, "subject"))
-                       v->s = copy_subject(subpos, sublen);
-               else if (!strcmp(name, "contents:subject"))
-                       v->s = copy_subject(subpos, sublen);
-               else if (!strcmp(name, "body"))
-                       v->s = xmemdupz(bodypos, bodylen);
-               else if (!strcmp(name, "contents:body"))
-                       v->s = xmemdupz(bodypos, nonsiglen);
-               else if (!strcmp(name, "contents:signature"))
-                       v->s = xmemdupz(sigpos, siglen);
-               else if (!strcmp(name, "contents"))
-                       v->s = xstrdup(subpos);
-       }
-}
-
-/*
- * We want to have empty print-string for field requests
- * that do not apply (e.g. "authordate" for a tag object)
- */
-static void fill_missing_values(struct atom_value *val)
-{
-       int i;
-       for (i = 0; i < used_atom_cnt; i++) {
-               struct atom_value *v = &val[i];
-               if (v->s == NULL)
-                       v->s = "";
-       }
-}
-
-/*
- * val is a list of atom_value to hold returned values.  Extract
- * the values for atoms in used_atom array out of (obj, buf, sz).
- * when deref is false, (obj, buf, sz) is the object that is
- * pointed at by the ref itself; otherwise it is the object the
- * ref (which is a tag) refers to.
- */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
-{
-       grab_common_values(val, deref, obj, buf, sz);
-       switch (obj->type) {
-       case OBJ_TAG:
-               grab_tag_values(val, deref, obj, buf, sz);
-               grab_sub_body_contents(val, deref, obj, buf, sz);
-               grab_person("tagger", val, deref, obj, buf, sz);
-               break;
-       case OBJ_COMMIT:
-               grab_commit_values(val, deref, obj, buf, sz);
-               grab_sub_body_contents(val, deref, obj, buf, sz);
-               grab_person("author", val, deref, obj, buf, sz);
-               grab_person("committer", val, deref, obj, buf, sz);
-               break;
-       case OBJ_TREE:
-               /* grab_tree_values(val, deref, obj, buf, sz); */
-               break;
-       case OBJ_BLOB:
-               /* grab_blob_values(val, deref, obj, buf, sz); */
-               break;
-       default:
-               die("Eh?  Object of type %d?", obj->type);
-       }
-}
-
-static inline char *copy_advance(char *dst, const char *src)
-{
-       while (*src)
-               *dst++ = *src++;
-       return dst;
-}
-
-/*
- * Parse the object referred by ref, and grab needed value.
- */
-static void populate_value(struct refinfo *ref)
-{
-       void *buf;
-       struct object *obj;
-       int eaten, i;
-       unsigned long size;
-       const unsigned char *tagged;
-
-       ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));
-
-       if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
-               unsigned char unused1[20];
-               ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
-                                            unused1, NULL);
-               if (!ref->symref)
-                       ref->symref = "";
-       }
-
-       /* Fill in specials first */
-       for (i = 0; i < used_atom_cnt; i++) {
-               const char *name = used_atom[i];
-               struct atom_value *v = &ref->value[i];
-               int deref = 0;
-               const char *refname;
-               const char *formatp;
-               struct branch *branch = NULL;
-
-               if (*name == '*') {
-                       deref = 1;
-                       name++;
-               }
-
-               if (starts_with(name, "refname"))
-                       refname = ref->refname;
-               else if (starts_with(name, "symref"))
-                       refname = ref->symref ? ref->symref : "";
-               else if (starts_with(name, "upstream")) {
-                       const char *branch_name;
-                       /* only local branches may have an upstream */
-                       if (!skip_prefix(ref->refname, "refs/heads/",
-                                        &branch_name))
-                               continue;
-                       branch = branch_get(branch_name);
-
-                       refname = branch_get_upstream(branch, NULL);
-                       if (!refname)
-                               continue;
-               } else if (starts_with(name, "push")) {
-                       const char *branch_name;
-                       if (!skip_prefix(ref->refname, "refs/heads/",
-                                        &branch_name))
-                               continue;
-                       branch = branch_get(branch_name);
-
-                       refname = branch_get_push(branch, NULL);
-                       if (!refname)
-                               continue;
-               } else if (starts_with(name, "color:")) {
-                       char color[COLOR_MAXLEN] = "";
-
-                       if (color_parse(name + 6, color) < 0)
-                               die(_("unable to parse format"));
-                       v->s = xstrdup(color);
-                       continue;
-               } else if (!strcmp(name, "flag")) {
-                       char buf[256], *cp = buf;
-                       if (ref->flag & REF_ISSYMREF)
-                               cp = copy_advance(cp, ",symref");
-                       if (ref->flag & REF_ISPACKED)
-                               cp = copy_advance(cp, ",packed");
-                       if (cp == buf)
-                               v->s = "";
-                       else {
-                               *cp = '\0';
-                               v->s = xstrdup(buf + 1);
-                       }
-                       continue;
-               } else if (!deref && grab_objectname(name, ref->objectname, v)) {
-                       continue;
-               } else if (!strcmp(name, "HEAD")) {
-                       const char *head;
-                       unsigned char sha1[20];
-
-                       head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-                                                 sha1, NULL);
-                       if (!strcmp(ref->refname, head))
-                               v->s = "*";
-                       else
-                               v->s = " ";
-                       continue;
-               } else
-                       continue;
-
-               formatp = strchr(name, ':');
-               if (formatp) {
-                       int num_ours, num_theirs;
-
-                       formatp++;
-                       if (!strcmp(formatp, "short"))
-                               refname = shorten_unambiguous_ref(refname,
-                                                     warn_ambiguous_refs);
-                       else if (!strcmp(formatp, "track") &&
-                                (starts_with(name, "upstream") ||
-                                 starts_with(name, "push"))) {
-                               char buf[40];
-
-                               if (stat_tracking_info(branch, &num_ours,
-                                                      &num_theirs, NULL))
-                                       continue;
-
-                               if (!num_ours && !num_theirs)
-                                       v->s = "";
-                               else if (!num_ours) {
-                                       sprintf(buf, "[behind %d]", num_theirs);
-                                       v->s = xstrdup(buf);
-                               } else if (!num_theirs) {
-                                       sprintf(buf, "[ahead %d]", num_ours);
-                                       v->s = xstrdup(buf);
-                               } else {
-                                       sprintf(buf, "[ahead %d, behind %d]",
-                                               num_ours, num_theirs);
-                                       v->s = xstrdup(buf);
-                               }
-                               continue;
-                       } else if (!strcmp(formatp, "trackshort") &&
-                                  (starts_with(name, "upstream") ||
-                                   starts_with(name, "push"))) {
-                               assert(branch);
-
-                               if (stat_tracking_info(branch, &num_ours,
-                                                       &num_theirs, NULL))
-                                       continue;
-
-                               if (!num_ours && !num_theirs)
-                                       v->s = "=";
-                               else if (!num_ours)
-                                       v->s = "<";
-                               else if (!num_theirs)
-                                       v->s = ">";
-                               else
-                                       v->s = "<>";
-                               continue;
-                       } else
-                               die("unknown %.*s format %s",
-                                   (int)(formatp - name), name, formatp);
-               }
-
-               if (!deref)
-                       v->s = refname;
-               else {
-                       int len = strlen(refname);
-                       char *s = xmalloc(len + 4);
-                       sprintf(s, "%s^{}", refname);
-                       v->s = s;
-               }
-       }
-
-       for (i = 0; i < used_atom_cnt; i++) {
-               struct atom_value *v = &ref->value[i];
-               if (v->s == NULL)
-                       goto need_obj;
-       }
-       return;
-
- need_obj:
-       buf = get_obj(ref->objectname, &obj, &size, &eaten);
-       if (!buf)
-               die("missing object %s for %s",
-                   sha1_to_hex(ref->objectname), ref->refname);
-       if (!obj)
-               die("parse_object_buffer failed on %s for %s",
-                   sha1_to_hex(ref->objectname), ref->refname);
-
-       grab_values(ref->value, 0, obj, buf, size);
-       if (!eaten)
-               free(buf);
-
-       /*
-        * If there is no atom that wants to know about tagged
-        * object, we are done.
-        */
-       if (!need_tagged || (obj->type != OBJ_TAG))
-               return;
-
-       /*
-        * If it is a tag object, see if we use a value that derefs
-        * the object, and if we do grab the object it refers to.
-        */
-       tagged = ((struct tag *)obj)->tagged->sha1;
-
-       /*
-        * NEEDSWORK: This derefs tag only once, which
-        * is good to deal with chains of trust, but
-        * is not consistent with what deref_tag() does
-        * which peels the onion to the core.
-        */
-       buf = get_obj(tagged, &obj, &size, &eaten);
-       if (!buf)
-               die("missing object %s for %s",
-                   sha1_to_hex(tagged), ref->refname);
-       if (!obj)
-               die("parse_object_buffer failed on %s for %s",
-                   sha1_to_hex(tagged), ref->refname);
-       grab_values(ref->value, 1, obj, buf, size);
-       if (!eaten)
-               free(buf);
-}
-
-/*
- * Given a ref, return the value for the atom.  This lazily gets value
- * out of the object by calling populate value.
- */
-static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
-{
-       if (!ref->value) {
-               populate_value(ref);
-               fill_missing_values(ref->value);
-       }
-       *v = &ref->value[atom];
-}
-
-struct grab_ref_cbdata {
-       struct refinfo **grab_array;
-       const char **grab_pattern;
-       int grab_cnt;
-};
-
-/*
- * A call-back given to for_each_ref().  Filter refs and keep them for
- * later object processing.
- */
-static int grab_single_ref(const char *refname, const struct object_id *oid,
-                          int flag, void *cb_data)
-{
-       struct grab_ref_cbdata *cb = cb_data;
-       struct refinfo *ref;
-       int cnt;
-
-       if (flag & REF_BAD_NAME) {
-                 warning("ignoring ref with broken name %s", refname);
-                 return 0;
-       }
-
-       if (flag & REF_ISBROKEN) {
-                 warning("ignoring broken ref %s", refname);
-                 return 0;
-       }
-
-       if (*cb->grab_pattern) {
-               const char **pattern;
-               int namelen = strlen(refname);
-               for (pattern = cb->grab_pattern; *pattern; pattern++) {
-                       const char *p = *pattern;
-                       int plen = strlen(p);
-
-                       if ((plen <= namelen) &&
-                           !strncmp(refname, p, plen) &&
-                           (refname[plen] == '\0' ||
-                            refname[plen] == '/' ||
-                            p[plen-1] == '/'))
-                               break;
-                       if (!wildmatch(p, refname, WM_PATHNAME, NULL))
-                               break;
-               }
-               if (!*pattern)
-                       return 0;
-       }
-
-       /*
-        * We do not open the object yet; sort may only need refname
-        * to do its job and the resulting list may yet to be pruned
-        * by maxcount logic.
-        */
-       ref = xcalloc(1, sizeof(*ref));
-       ref->refname = xstrdup(refname);
-       hashcpy(ref->objectname, oid->hash);
-       ref->flag = flag;
-
-       cnt = cb->grab_cnt;
-       REALLOC_ARRAY(cb->grab_array, cnt + 1);
-       cb->grab_array[cnt++] = ref;
-       cb->grab_cnt = cnt;
-       return 0;
-}
-
-static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
-{
-       struct atom_value *va, *vb;
-       int cmp;
-       cmp_type cmp_type = used_atom_type[s->atom];
-
-       get_value(a, s->atom, &va);
-       get_value(b, s->atom, &vb);
-       switch (cmp_type) {
-       case FIELD_STR:
-               cmp = strcmp(va->s, vb->s);
-               break;
-       default:
-               if (va->ul < vb->ul)
-                       cmp = -1;
-               else if (va->ul == vb->ul)
-                       cmp = 0;
-               else
-                       cmp = 1;
-               break;
-       }
-       return (s->reverse) ? -cmp : cmp;
-}
-
-static struct ref_sort *ref_sort;
-static int compare_refs(const void *a_, const void *b_)
-{
-       struct refinfo *a = *((struct refinfo **)a_);
-       struct refinfo *b = *((struct refinfo **)b_);
-       struct ref_sort *s;
-
-       for (s = ref_sort; s; s = s->next) {
-               int cmp = cmp_ref_sort(s, a, b);
-               if (cmp)
-                       return cmp;
-       }
-       return 0;
-}
-
-static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)
-{
-       ref_sort = sort;
-       qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
-}
-
-static void print_value(struct atom_value *v, int quote_style)
-{
-       struct strbuf sb = STRBUF_INIT;
-       switch (quote_style) {
-       case QUOTE_NONE:
-               fputs(v->s, stdout);
-               break;
-       case QUOTE_SHELL:
-               sq_quote_buf(&sb, v->s);
-               break;
-       case QUOTE_PERL:
-               perl_quote_buf(&sb, v->s);
-               break;
-       case QUOTE_PYTHON:
-               python_quote_buf(&sb, v->s);
-               break;
-       case QUOTE_TCL:
-               tcl_quote_buf(&sb, v->s);
-               break;
-       }
-       if (quote_style != QUOTE_NONE) {
-               fputs(sb.buf, stdout);
-               strbuf_release(&sb);
-       }
-}
-
-static int hex1(char ch)
-{
-       if ('0' <= ch && ch <= '9')
-               return ch - '0';
-       else if ('a' <= ch && ch <= 'f')
-               return ch - 'a' + 10;
-       else if ('A' <= ch && ch <= 'F')
-               return ch - 'A' + 10;
-       return -1;
-}
-static int hex2(const char *cp)
-{
-       if (cp[0] && cp[1])
-               return (hex1(cp[0]) << 4) | hex1(cp[1]);
-       else
-               return -1;
-}
-
-static void emit(const char *cp, const char *ep)
-{
-       while (*cp && (!ep || cp < ep)) {
-               if (*cp == '%') {
-                       if (cp[1] == '%')
-                               cp++;
-                       else {
-                               int ch = hex2(cp + 1);
-                               if (0 <= ch) {
-                                       putchar(ch);
-                                       cp += 3;
-                                       continue;
-                               }
-                       }
-               }
-               putchar(*cp);
-               cp++;
-       }
-}
-
-static void show_ref(struct refinfo *info, const char *format, int quote_style)
-{
-       const char *cp, *sp, *ep;
-
-       for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
-               struct atom_value *atomv;
-
-               ep = strchr(sp, ')');
-               if (cp < sp)
-                       emit(cp, sp);
-               get_value(info, parse_atom(sp + 2, ep), &atomv);
-               print_value(atomv, quote_style);
-       }
-       if (*cp) {
-               sp = cp + strlen(cp);
-               emit(cp, sp);
-       }
-       if (need_color_reset_at_eol) {
-               struct atom_value resetv;
-               char color[COLOR_MAXLEN] = "";
-
-               if (color_parse("reset", color) < 0)
-                       die("BUG: couldn't parse 'reset' as a color");
-               resetv.s = color;
-               print_value(&resetv, quote_style);
-       }
-       putchar('\n');
-}
-
-static struct ref_sort *default_sort(void)
-{
-       static const char cstr_name[] = "refname";
-
-       struct ref_sort *sort = xcalloc(1, sizeof(*sort));
-
-       sort->next = NULL;
-       sort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));
-       return sort;
-}
-
-static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
-{
-       struct ref_sort **sort_tail = opt->value;
-       struct ref_sort *s;
-       int len;
-
-       if (!arg) /* should --no-sort void the list ? */
-               return -1;
-
-       s = xcalloc(1, sizeof(*s));
-       s->next = *sort_tail;
-       *sort_tail = s;
-
-       if (*arg == '-') {
-               s->reverse = 1;
-               arg++;
-       }
-       len = strlen(arg);
-       s->atom = parse_atom(arg, arg+len);
-       return 0;
-}
+#include "ref-filter.h"
 
 static char const * const for_each_ref_usage[] = {
        N_("git for-each-ref [<options>] [<pattern>]"),
@@ -1086,12 +12,12 @@ static char const * const for_each_ref_usage[] = {
 
 int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 {
-       int i, num_refs;
+       int i;
        const char *format = "%(objectname) %(objecttype)\t%(refname)";
-       struct ref_sort *sort = NULL, **sort_tail = &sort;
+       struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
        int maxcount = 0, quote_style = 0;
-       struct refinfo **refs;
-       struct grab_ref_cbdata cbdata;
+       struct ref_array array;
+       struct ref_filter filter;
 
        struct option opts[] = {
                OPT_BIT('s', "shell", &quote_style,
@@ -1106,8 +32,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                OPT_GROUP(""),
                OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
                OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
-               OPT_CALLBACK(0 , "sort", sort_tail, N_("key"),
-                           N_("field name to sort on"), &opt_parse_sort),
+               OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
+                           N_("field name to sort on"), &parse_opt_ref_sorting),
                OPT_END(),
        };
 
@@ -1120,26 +46,25 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                error("more than one quoting style?");
                usage_with_options(for_each_ref_usage, opts);
        }
-       if (verify_format(format))
+       if (verify_ref_format(format))
                usage_with_options(for_each_ref_usage, opts);
 
-       if (!sort)
-               sort = default_sort();
+       if (!sorting)
+               sorting = ref_default_sorting();
 
        /* for warn_ambiguous_refs */
        git_config(git_default_config, NULL);
 
-       memset(&cbdata, 0, sizeof(cbdata));
-       cbdata.grab_pattern = argv;
-       for_each_rawref(grab_single_ref, &cbdata);
-       refs = cbdata.grab_array;
-       num_refs = cbdata.grab_cnt;
-
-       sort_refs(sort, refs, num_refs);
+       memset(&array, 0, sizeof(array));
+       memset(&filter, 0, sizeof(filter));
+       filter.name_patterns = argv;
+       filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
+       ref_array_sort(sorting, &array);
 
-       if (!maxcount || num_refs < maxcount)
-               maxcount = num_refs;
+       if (!maxcount || array.nr < maxcount)
+               maxcount = array.nr;
        for (i = 0; i < maxcount; i++)
-               show_ref(refs[i], format, quote_style);
+               show_ref_array_item(array.items[i], format, quote_style);
+       ref_array_clear(&array);
        return 0;
 }
index 2679793..f4b87e9 100644 (file)
@@ -23,8 +23,11 @@ static int show_tags;
 static int show_unreachable;
 static int include_reflogs = 1;
 static int check_full = 1;
+static int connectivity_only;
 static int check_strict;
 static int keep_cache_objects;
+static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;
+static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;
 static struct object_id head_oid;
 static const char *head_points_at;
 static int errors_found;
@@ -44,39 +47,52 @@ static int show_dangling = 1;
 #define DIRENT_SORT_HINT(de) ((de)->d_ino)
 #endif
 
-static void objreport(struct object *obj, const char *severity,
-                      const char *err, va_list params)
+static int fsck_config(const char *var, const char *value, void *cb)
 {
-       fprintf(stderr, "%s in %s %s: ",
-               severity, typename(obj->type), sha1_to_hex(obj->sha1));
-       vfprintf(stderr, err, params);
-       fputs("\n", stderr);
+       if (strcmp(var, "fsck.skiplist") == 0) {
+               const char *path;
+               struct strbuf sb = STRBUF_INIT;
+
+               if (git_config_pathname(&path, var, value))
+                       return 1;
+               strbuf_addf(&sb, "skiplist=%s", path);
+               free((char *)path);
+               fsck_set_msg_types(&fsck_obj_options, sb.buf);
+               strbuf_release(&sb);
+               return 0;
+       }
+
+       if (skip_prefix(var, "fsck.", &var)) {
+               fsck_set_msg_type(&fsck_obj_options, var, value);
+               return 0;
+       }
+
+       return git_default_config(var, value, cb);
+}
+
+static void objreport(struct object *obj, const char *msg_type,
+                       const char *err)
+{
+       fprintf(stderr, "%s in %s %s: %s\n",
+               msg_type, typename(obj->type), sha1_to_hex(obj->sha1), err);
 }
 
-__attribute__((format (printf, 2, 3)))
-static int objerror(struct object *obj, const char *err, ...)
+static int objerror(struct object *obj, const char *err)
 {
-       va_list params;
-       va_start(params, err);
        errors_found |= ERROR_OBJECT;
-       objreport(obj, "error", err, params);
-       va_end(params);
+       objreport(obj, "error", err);
        return -1;
 }
 
-__attribute__((format (printf, 3, 4)))
-static int fsck_error_func(struct object *obj, int type, const char *err, ...)
+static int fsck_error_func(struct object *obj, int type, const char *message)
 {
-       va_list params;
-       va_start(params, err);
-       objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params);
-       va_end(params);
+       objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
        return (type == FSCK_WARN) ? 0 : 1;
 }
 
 static struct object_array pending;
 
-static int mark_object(struct object *obj, int type, void *data)
+static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options)
 {
        struct object *parent = data;
 
@@ -119,7 +135,7 @@ static int mark_object(struct object *obj, int type, void *data)
 
 static void mark_object_reachable(struct object *obj)
 {
-       mark_object(obj, OBJ_ANY, NULL);
+       mark_object(obj, OBJ_ANY, NULL, NULL);
 }
 
 static int traverse_one_object(struct object *obj)
@@ -132,7 +148,7 @@ static int traverse_one_object(struct object *obj)
                if (parse_tree(tree) < 0)
                        return 1; /* error already displayed */
        }
-       result = fsck_walk(obj, mark_object, obj);
+       result = fsck_walk(obj, obj, &fsck_walk_options);
        if (tree)
                free_tree_buffer(tree);
        return result;
@@ -158,7 +174,7 @@ static int traverse_reachable(void)
        return !!result;
 }
 
-static int mark_used(struct object *obj, int type, void *data)
+static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options)
 {
        if (!obj)
                return 1;
@@ -179,6 +195,8 @@ static void check_reachable_object(struct object *obj)
        if (!(obj->flags & HAS_OBJ)) {
                if (has_sha1_pack(obj->sha1))
                        return; /* it is in pack - forget about it */
+               if (connectivity_only && has_sha1_file(obj->sha1))
+                       return;
                printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
                errors_found |= ERROR_REACHABLE;
                return;
@@ -296,9 +314,9 @@ static int fsck_obj(struct object *obj)
                fprintf(stderr, "Checking %s %s\n",
                        typename(obj->type), sha1_to_hex(obj->sha1));
 
-       if (fsck_walk(obj, mark_used, NULL))
+       if (fsck_walk(obj, NULL, &fsck_obj_options))
                objerror(obj, "broken links");
-       if (fsck_object(obj, NULL, 0, check_strict, fsck_error_func))
+       if (fsck_object(obj, NULL, 0, &fsck_obj_options))
                return -1;
 
        if (obj->type == OBJ_TREE) {
@@ -621,6 +639,7 @@ static struct option fsck_opts[] = {
        OPT_BOOL(0, "cache", &keep_cache_objects, N_("make index objects head nodes")),
        OPT_BOOL(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")),
        OPT_BOOL(0, "full", &check_full, N_("also consider packs and alternate objects")),
+       OPT_BOOL(0, "connectivity-only", &connectivity_only, N_("check only connectivity")),
        OPT_BOOL(0, "strict", &check_strict, N_("enable more strict checking")),
        OPT_BOOL(0, "lost-found", &write_lost_and_found,
                                N_("write dangling objects in .git/lost-found")),
@@ -638,6 +657,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
 
+       fsck_walk_options.walk = mark_object;
+       fsck_obj_options.walk = mark_used;
+       fsck_obj_options.error_func = fsck_error_func;
+       if (check_strict)
+               fsck_obj_options.strict = 1;
+
        if (show_progress == -1)
                show_progress = isatty(2);
        if (verbose)
@@ -648,8 +673,11 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                include_reflogs = 0;
        }
 
+       git_config(fsck_config, NULL);
+
        fsck_head_link();
-       fsck_object_dir(get_object_directory());
+       if (!connectivity_only)
+               fsck_object_dir(get_object_directory());
 
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {
index 4957c39..bcc75d9 100644 (file)
@@ -85,7 +85,7 @@ static void gc_config(void)
        git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
        git_config_get_bool("gc.autodetach", &detach_auto);
        git_config_date_string("gc.pruneexpire", &prune_expire);
-       git_config_date_string("gc.pruneworktreesexpire", &prune_worktrees_expire);
+       git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire);
        git_config(git_default_config, NULL);
 }
 
index f07bc66..3f10840 100644 (file)
@@ -75,6 +75,7 @@ static int nr_threads;
 static int from_stdin;
 static int strict;
 static int do_fsck_object;
+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
 static int verbose;
 static int show_stat;
 static int check_self_contained_and_connected;
@@ -192,7 +193,7 @@ static void cleanup_thread(void)
 #endif
 
 
-static int mark_link(struct object *obj, int type, void *data)
+static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)
 {
        if (!obj)
                return -1;
@@ -838,10 +839,9 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
                        if (!obj)
                                die(_("invalid %s"), typename(type));
                        if (do_fsck_object &&
-                           fsck_object(obj, buf, size, 1,
-                                   fsck_error_function))
+                           fsck_object(obj, buf, size, &fsck_options))
                                die(_("Error in object"));
-                       if (fsck_walk(obj, mark_link, NULL))
+                       if (fsck_walk(obj, NULL, &fsck_options))
                                die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1));
 
                        if (obj->type == OBJ_TREE) {
@@ -1615,6 +1615,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                usage(index_pack_usage);
 
        check_replace_refs = 0;
+       fsck_options.walk = mark_link;
 
        reset_pack_idx_option(&opts);
        git_config(git_index_pack_config, &opts);
@@ -1632,6 +1633,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                        } else if (!strcmp(arg, "--strict")) {
                                strict = 1;
                                do_fsck_object = 1;
+                       } else if (skip_prefix(arg, "--strict=", &arg)) {
+                               strict = 1;
+                               do_fsck_object = 1;
+                               fsck_set_msg_types(&fsck_options, arg);
                        } else if (!strcmp(arg, "--check-self-contained-and-connected")) {
                                strict = 1;
                                check_self_contained_and_connected = 1;
index 4335738..49df78d 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "cache.h"
+#include "refs.h"
 #include "builtin.h"
 #include "exec_cmd.h"
 #include "parse-options.h"
index 8781049..b50ef75 100644 (file)
@@ -5,6 +5,7 @@
  *              2006 Junio Hamano
  */
 #include "cache.h"
+#include "refs.h"
 #include "color.h"
 #include "commit.h"
 #include "diff.h"
@@ -31,6 +32,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 decoration_style;
 static int decoration_given;
 static int use_mailmap_config;
@@ -102,6 +104,8 @@ static void cmd_log_init_defaults(struct rev_info *rev)
 {
        if (fmt_pretty)
                get_commit_format(fmt_pretty, rev);
+       if (default_follow)
+               DIFF_OPT_SET(&rev->diffopt, DEFAULT_FOLLOW_RENAMES);
        rev->verbose_header = 1;
        DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
        rev->diffopt.stat_width = -1; /* use full terminal width */
@@ -112,7 +116,7 @@ static void cmd_log_init_defaults(struct rev_info *rev)
        DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
 
        if (default_date_mode)
-               rev->date_mode = parse_date_format(default_date_mode);
+               parse_date_format(default_date_mode, &rev->date_mode);
        rev->diffopt.touched_flags = 0;
 }
 
@@ -390,6 +394,10 @@ static int git_log_config(const char *var, const char *value, void *cb)
                default_show_root = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "log.follow")) {
+               default_follow = git_config_bool(var, value);
+               return 0;
+       }
        if (skip_prefix(var, "color.decorate.", &slot_name))
                return parse_decorate_color_config(var, slot_name, value);
        if (!strcmp(var, "log.mailmap")) {
@@ -618,6 +626,14 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
        return cmd_log_walk(&rev);
 }
 
+static void default_follow_tweak(struct rev_info *rev,
+                                struct setup_revision_opt *opt)
+{
+       if (DIFF_OPT_TST(&rev->diffopt, DEFAULT_FOLLOW_RENAMES) &&
+           rev->prune_data.nr == 1)
+               DIFF_OPT_SET(&rev->diffopt, FOLLOW_RENAMES);
+}
+
 int cmd_log(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
@@ -631,6 +647,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
        opt.revarg_opt = REVARG_COMMITTISH;
+       opt.tweak = default_follow_tweak;
        cmd_log_init(argc, argv, prefix, &rev, &opt);
        return cmd_log_walk(&rev);
 }
@@ -939,7 +956,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
 
        msg = body;
        pp.fmt = CMIT_FMT_EMAIL;
-       pp.date_mode = DATE_RFC2822;
+       pp.date_mode.type = DATE_RFC2822;
        pp_user_info(&pp, NULL, &sb, committer, encoding);
        pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
        pp_remainder(&pp, &msg, &sb, 0);
index 80fe8c7..62cc16d 100644 (file)
@@ -2588,23 +2588,6 @@ static int option_parse_unpack_unreachable(const struct option *opt,
        return 0;
 }
 
-static int option_parse_ulong(const struct option *opt,
-                             const char *arg, int unset)
-{
-       if (unset)
-               die(_("option %s does not accept negative form"),
-                   opt->long_name);
-
-       if (!git_parse_ulong(arg, opt->value))
-               die(_("unable to parse value '%s' for option %s"),
-                   arg, opt->long_name);
-       return 0;
-}
-
-#define OPT_ULONG(s, l, v, h) \
-       { OPTION_CALLBACK, (s), (l), (v), "n", (h),     \
-         PARSE_OPT_NONEG, option_parse_ulong }
-
 int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 {
        int use_internal_rev_list = 0;
@@ -2627,16 +2610,16 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 0, "index-version", NULL, N_("version[,offset]"),
                  N_("write the pack index file in the specified idx format version"),
                  0, option_parse_index_version },
-               OPT_ULONG(0, "max-pack-size", &pack_size_limit,
-                         N_("maximum size of each output pack file")),
+               OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit,
+                             N_("maximum size of each output pack file")),
                OPT_BOOL(0, "local", &local,
                         N_("ignore borrowed objects from alternate object store")),
                OPT_BOOL(0, "incremental", &incremental,
                         N_("ignore packed objects")),
                OPT_INTEGER(0, "window", &window,
                            N_("limit pack window by objects")),
-               OPT_ULONG(0, "window-memory", &window_memory_limit,
-                         N_("limit pack window by memory in addition to object limit")),
+               OPT_MAGNITUDE(0, "window-memory", &window_memory_limit,
+                             N_("limit pack window by memory in addition to object limit")),
                OPT_INTEGER(0, "depth", &depth,
                            N_("maximum length of delta chain allowed in the resulting pack")),
                OPT_BOOL(0, "reuse-delta", &reuse_delta,
diff --git a/builtin/pull.c b/builtin/pull.c
new file mode 100644 (file)
index 0000000..b7bc1ff
--- /dev/null
@@ -0,0 +1,886 @@
+/*
+ * Builtin "git pull"
+ *
+ * Based on git-pull.sh by Junio C Hamano
+ *
+ * Fetch one or more remote refs and merge it/them into the current HEAD.
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "exec_cmd.h"
+#include "run-command.h"
+#include "sha1-array.h"
+#include "remote.h"
+#include "dir.h"
+#include "refs.h"
+#include "revision.h"
+#include "lockfile.h"
+
+enum rebase_type {
+       REBASE_INVALID = -1,
+       REBASE_FALSE = 0,
+       REBASE_TRUE,
+       REBASE_PRESERVE
+};
+
+/**
+ * Parses the value of --rebase. If value is a false value, returns
+ * REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is
+ * "preserve", returns REBASE_PRESERVE. If value is a invalid value, dies with
+ * a fatal error if fatal is true, otherwise returns REBASE_INVALID.
+ */
+static enum rebase_type parse_config_rebase(const char *key, const char *value,
+               int fatal)
+{
+       int v = git_config_maybe_bool("pull.rebase", value);
+
+       if (!v)
+               return REBASE_FALSE;
+       else if (v > 0)
+               return REBASE_TRUE;
+       else if (!strcmp(value, "preserve"))
+               return REBASE_PRESERVE;
+
+       if (fatal)
+               die(_("Invalid value for %s: %s"), key, value);
+       else
+               error(_("Invalid value for %s: %s"), key, value);
+
+       return REBASE_INVALID;
+}
+
+/**
+ * Callback for --rebase, which parses arg with parse_config_rebase().
+ */
+static int parse_opt_rebase(const struct option *opt, const char *arg, int unset)
+{
+       enum rebase_type *value = opt->value;
+
+       if (arg)
+               *value = parse_config_rebase("--rebase", arg, 0);
+       else
+               *value = unset ? REBASE_FALSE : REBASE_TRUE;
+       return *value == REBASE_INVALID ? -1 : 0;
+}
+
+static const char * const pull_usage[] = {
+       N_("git pull [options] [<repository> [<refspec>...]]"),
+       NULL
+};
+
+/* Shared options */
+static int opt_verbosity;
+static char *opt_progress;
+
+/* Options passed to git-merge or git-rebase */
+static enum rebase_type opt_rebase = -1;
+static char *opt_diffstat;
+static char *opt_log;
+static char *opt_squash;
+static char *opt_commit;
+static char *opt_edit;
+static char *opt_ff;
+static char *opt_verify_signatures;
+static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
+static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
+static char *opt_gpg_sign;
+
+/* Options passed to git-fetch */
+static char *opt_all;
+static char *opt_append;
+static char *opt_upload_pack;
+static int opt_force;
+static char *opt_tags;
+static char *opt_prune;
+static char *opt_recurse_submodules;
+static int opt_dry_run;
+static char *opt_keep;
+static char *opt_depth;
+static char *opt_unshallow;
+static char *opt_update_shallow;
+static char *opt_refmap;
+
+static struct option pull_options[] = {
+       /* Shared options */
+       OPT__VERBOSITY(&opt_verbosity),
+       OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
+               N_("force progress reporting"),
+               PARSE_OPT_NOARG),
+
+       /* Options passed to git-merge or git-rebase */
+       OPT_GROUP(N_("Options related to merging")),
+       { OPTION_CALLBACK, 'r', "rebase", &opt_rebase,
+         N_("false|true|preserve"),
+         N_("incorporate changes by rebasing rather than merging"),
+         PARSE_OPT_OPTARG, parse_opt_rebase },
+       OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
+               N_("do not show a diffstat at the end of the merge"),
+               PARSE_OPT_NOARG | PARSE_OPT_NONEG),
+       OPT_PASSTHRU(0, "stat", &opt_diffstat, NULL,
+               N_("show a diffstat at the end of the merge"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU(0, "summary", &opt_diffstat, NULL,
+               N_("(synonym to --stat)"),
+               PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
+       OPT_PASSTHRU(0, "log", &opt_log, N_("n"),
+               N_("add (at most <n>) entries from shortlog to merge commit message"),
+               PARSE_OPT_OPTARG),
+       OPT_PASSTHRU(0, "squash", &opt_squash, NULL,
+               N_("create a single commit instead of doing a merge"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU(0, "commit", &opt_commit, NULL,
+               N_("perform a commit if the merge succeeds (default)"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU(0, "edit", &opt_edit, NULL,
+               N_("edit message before committing"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU(0, "ff", &opt_ff, NULL,
+               N_("allow fast-forward"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU(0, "ff-only", &opt_ff, NULL,
+               N_("abort if fast-forward is not possible"),
+               PARSE_OPT_NOARG | PARSE_OPT_NONEG),
+       OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
+               N_("verify that the named commit has a valid GPG signature"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
+               N_("merge strategy to use"),
+               0),
+       OPT_PASSTHRU_ARGV('X', "strategy-option", &opt_strategy_opts,
+               N_("option=value"),
+               N_("option for selected merge strategy"),
+               0),
+       OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"),
+               N_("GPG sign commit"),
+               PARSE_OPT_OPTARG),
+
+       /* Options passed to git-fetch */
+       OPT_GROUP(N_("Options related to fetching")),
+       OPT_PASSTHRU(0, "all", &opt_all, NULL,
+               N_("fetch from all remotes"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU('a', "append", &opt_append, NULL,
+               N_("append to .git/FETCH_HEAD instead of overwriting"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"),
+               N_("path to upload pack on remote end"),
+               0),
+       OPT__FORCE(&opt_force, N_("force overwrite of local branch")),
+       OPT_PASSTHRU('t', "tags", &opt_tags, NULL,
+               N_("fetch all tags and associated objects"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU('p', "prune", &opt_prune, NULL,
+               N_("prune remote-tracking branches no longer on remote"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU(0, "recurse-submodules", &opt_recurse_submodules,
+               N_("on-demand"),
+               N_("control recursive fetching of submodules"),
+               PARSE_OPT_OPTARG),
+       OPT_BOOL(0, "dry-run", &opt_dry_run,
+               N_("dry run")),
+       OPT_PASSTHRU('k', "keep", &opt_keep, NULL,
+               N_("keep downloaded pack"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU(0, "depth", &opt_depth, N_("depth"),
+               N_("deepen history of shallow clone"),
+               0),
+       OPT_PASSTHRU(0, "unshallow", &opt_unshallow, NULL,
+               N_("convert to a complete repository"),
+               PARSE_OPT_NONEG | PARSE_OPT_NOARG),
+       OPT_PASSTHRU(0, "update-shallow", &opt_update_shallow, NULL,
+               N_("accept refs that update .git/shallow"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU(0, "refmap", &opt_refmap, N_("refmap"),
+               N_("specify fetch refmap"),
+               PARSE_OPT_NONEG),
+
+       OPT_END()
+};
+
+/**
+ * Pushes "-q" or "-v" switches into arr to match the opt_verbosity level.
+ */
+static void argv_push_verbosity(struct argv_array *arr)
+{
+       int verbosity;
+
+       for (verbosity = opt_verbosity; verbosity > 0; verbosity--)
+               argv_array_push(arr, "-v");
+
+       for (verbosity = opt_verbosity; verbosity < 0; verbosity++)
+               argv_array_push(arr, "-q");
+}
+
+/**
+ * Pushes "-f" switches into arr to match the opt_force level.
+ */
+static void argv_push_force(struct argv_array *arr)
+{
+       int force = opt_force;
+       while (force-- > 0)
+               argv_array_push(arr, "-f");
+}
+
+/**
+ * Sets the GIT_REFLOG_ACTION environment variable to the concatenation of argv
+ */
+static void set_reflog_message(int argc, const char **argv)
+{
+       int i;
+       struct strbuf msg = STRBUF_INIT;
+
+       for (i = 0; i < argc; i++) {
+               if (i)
+                       strbuf_addch(&msg, ' ');
+               strbuf_addstr(&msg, argv[i]);
+       }
+
+       setenv("GIT_REFLOG_ACTION", msg.buf, 0);
+
+       strbuf_release(&msg);
+}
+
+/**
+ * If pull.ff is unset, returns NULL. If pull.ff is "true", returns "--ff". If
+ * pull.ff is "false", returns "--no-ff". If pull.ff is "only", returns
+ * "--ff-only". Otherwise, if pull.ff is set to an invalid value, die with an
+ * error.
+ */
+static const char *config_get_ff(void)
+{
+       const char *value;
+
+       if (git_config_get_value("pull.ff", &value))
+               return NULL;
+
+       switch (git_config_maybe_bool("pull.ff", value)) {
+       case 0:
+               return "--no-ff";
+       case 1:
+               return "--ff";
+       }
+
+       if (!strcmp(value, "only"))
+               return "--ff-only";
+
+       die(_("Invalid value for pull.ff: %s"), value);
+}
+
+/**
+ * Returns the default configured value for --rebase. It first looks for the
+ * value of "branch.$curr_branch.rebase", where $curr_branch is the current
+ * branch, and if HEAD is detached or the configuration key does not exist,
+ * looks for the value of "pull.rebase". If both configuration keys do not
+ * exist, returns REBASE_FALSE.
+ */
+static enum rebase_type config_get_rebase(void)
+{
+       struct branch *curr_branch = branch_get("HEAD");
+       const char *value;
+
+       if (curr_branch) {
+               char *key = xstrfmt("branch.%s.rebase", curr_branch->name);
+
+               if (!git_config_get_value(key, &value)) {
+                       enum rebase_type ret = parse_config_rebase(key, value, 1);
+                       free(key);
+                       return ret;
+               }
+
+               free(key);
+       }
+
+       if (!git_config_get_value("pull.rebase", &value))
+               return parse_config_rebase("pull.rebase", value, 1);
+
+       return REBASE_FALSE;
+}
+
+/**
+ * Returns 1 if there are unstaged changes, 0 otherwise.
+ */
+static int has_unstaged_changes(const char *prefix)
+{
+       struct rev_info rev_info;
+       int result;
+
+       init_revisions(&rev_info, prefix);
+       DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
+       DIFF_OPT_SET(&rev_info.diffopt, QUICK);
+       diff_setup_done(&rev_info.diffopt);
+       result = run_diff_files(&rev_info, 0);
+       return diff_result_code(&rev_info.diffopt, result);
+}
+
+/**
+ * Returns 1 if there are uncommitted changes, 0 otherwise.
+ */
+static int has_uncommitted_changes(const char *prefix)
+{
+       struct rev_info rev_info;
+       int result;
+
+       if (is_cache_unborn())
+               return 0;
+
+       init_revisions(&rev_info, prefix);
+       DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
+       DIFF_OPT_SET(&rev_info.diffopt, QUICK);
+       add_head_to_pending(&rev_info);
+       diff_setup_done(&rev_info.diffopt);
+       result = run_diff_index(&rev_info, 1);
+       return diff_result_code(&rev_info.diffopt, result);
+}
+
+/**
+ * If the work tree has unstaged or uncommitted changes, dies with the
+ * appropriate message.
+ */
+static void die_on_unclean_work_tree(const char *prefix)
+{
+       struct lock_file *lock_file = xcalloc(1, sizeof(*lock_file));
+       int do_die = 0;
+
+       hold_locked_index(lock_file, 0);
+       refresh_cache(REFRESH_QUIET);
+       update_index_if_able(&the_index, lock_file);
+       rollback_lock_file(lock_file);
+
+       if (has_unstaged_changes(prefix)) {
+               error(_("Cannot pull with rebase: You have unstaged changes."));
+               do_die = 1;
+       }
+
+       if (has_uncommitted_changes(prefix)) {
+               if (do_die)
+                       error(_("Additionally, your index contains uncommitted changes."));
+               else
+                       error(_("Cannot pull with rebase: Your index contains uncommitted changes."));
+               do_die = 1;
+       }
+
+       if (do_die)
+               exit(1);
+}
+
+/**
+ * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge
+ * into merge_heads.
+ */
+static void get_merge_heads(struct sha1_array *merge_heads)
+{
+       const char *filename = git_path("FETCH_HEAD");
+       FILE *fp;
+       struct strbuf sb = STRBUF_INIT;
+       unsigned char sha1[GIT_SHA1_RAWSZ];
+
+       if (!(fp = fopen(filename, "r")))
+               die_errno(_("could not open '%s' for reading"), filename);
+       while (strbuf_getline(&sb, fp, '\n') != EOF) {
+               if (get_sha1_hex(sb.buf, sha1))
+                       continue;  /* invalid line: does not start with SHA1 */
+               if (starts_with(sb.buf + GIT_SHA1_HEXSZ, "\tnot-for-merge\t"))
+                       continue;  /* ref is not-for-merge */
+               sha1_array_append(merge_heads, sha1);
+       }
+       fclose(fp);
+       strbuf_release(&sb);
+}
+
+/**
+ * Used by die_no_merge_candidates() as a for_each_remote() callback to
+ * retrieve the name of the remote if the repository only has one remote.
+ */
+static int get_only_remote(struct remote *remote, void *cb_data)
+{
+       const char **remote_name = cb_data;
+
+       if (*remote_name)
+               return -1;
+
+       *remote_name = remote->name;
+       return 0;
+}
+
+/**
+ * Dies with the appropriate reason for why there are no merge candidates:
+ *
+ * 1. We fetched from a specific remote, and a refspec was given, but it ended
+ *    up not fetching anything. This is usually because the user provided a
+ *    wildcard refspec which had no matches on the remote end.
+ *
+ * 2. We fetched from a non-default remote, but didn't specify a branch to
+ *    merge. We can't use the configured one because it applies to the default
+ *    remote, thus the user must specify the branches to merge.
+ *
+ * 3. We fetched from the branch's or repo's default remote, but:
+ *
+ *    a. We are not on a branch, so there will never be a configured branch to
+ *       merge with.
+ *
+ *    b. We are on a branch, but there is no configured branch to merge with.
+ *
+ * 4. We fetched from the branch's or repo's default remote, but the configured
+ *    branch to merge didn't get fetched. (Either it doesn't exist, or wasn't
+ *    part of the configured fetch refspec.)
+ */
+static void NORETURN die_no_merge_candidates(const char *repo, const char **refspecs)
+{
+       struct branch *curr_branch = branch_get("HEAD");
+       const char *remote = curr_branch ? curr_branch->remote_name : NULL;
+
+       if (*refspecs) {
+               if (opt_rebase)
+                       fprintf_ln(stderr, _("There is no candidate for rebasing against among the refs that you just fetched."));
+               else
+                       fprintf_ln(stderr, _("There are no candidates for merging among the refs that you just fetched."));
+               fprintf_ln(stderr, _("Generally this means that you provided a wildcard refspec which had no\n"
+                                       "matches on the remote end."));
+       } else if (repo && curr_branch && (!remote || strcmp(repo, remote))) {
+               fprintf_ln(stderr, _("You asked to pull from the remote '%s', but did not specify\n"
+                       "a branch. Because this is not the default configured remote\n"
+                       "for your current branch, you must specify a branch on the command line."),
+                       repo);
+       } else if (!curr_branch) {
+               fprintf_ln(stderr, _("You are not currently on a branch."));
+               if (opt_rebase)
+                       fprintf_ln(stderr, _("Please specify which branch you want to rebase against."));
+               else
+                       fprintf_ln(stderr, _("Please specify which branch you want to merge with."));
+               fprintf_ln(stderr, _("See git-pull(1) for details."));
+               fprintf(stderr, "\n");
+               fprintf_ln(stderr, "    git pull <remote> <branch>");
+               fprintf(stderr, "\n");
+       } else if (!curr_branch->merge_nr) {
+               const char *remote_name = NULL;
+
+               if (for_each_remote(get_only_remote, &remote_name) || !remote_name)
+                       remote_name = "<remote>";
+
+