Merge branch 'rs/child-process-init'
authorJunio C Hamano <gitster@pobox.com>
Fri, 12 Aug 2016 16:47:36 +0000 (09:47 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 12 Aug 2016 16:47:36 +0000 (09:47 -0700)
A small code clean-up.

* rs/child-process-init:
use CHILD_PROCESS_INIT to initialize automatic variables

90 files changed:
.mailmap
Documentation/RelNotes/2.10.0.txt
Documentation/RelNotes/2.9.3.txt
Documentation/config.txt
Documentation/git-pack-objects.txt
Documentation/git-push.txt
Documentation/git-repack.txt
Documentation/gitattributes.txt
Documentation/gitmodules.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/technical/api-hashmap.txt
Makefile
builtin/am.c
builtin/blame.c
builtin/checkout.c
builtin/config.c
builtin/fmt-merge-msg.c
builtin/log.c
builtin/ls-files.c
builtin/merge.c
builtin/notes.c
builtin/pack-objects.c
builtin/rev-parse.c
builtin/submodule--helper.c
builtin/update-index.c
cache.h
commit-slab.h
compat/nedmalloc/nedmalloc.c
config.c
config.mak.uname
contrib/completion/git-completion.bash
contrib/fast-import/import-tars.perl
contrib/subtree/git-subtree.sh
date.c
diffcore-rename.c
git-difftool.perl
git-rebase--interactive.sh
git-sh-setup.sh
git-submodule.sh
gitweb/gitweb.perl
grep.c
http-push.c
http.c
ident.c
imap-send.c
log-tree.c
merge-recursive.c
merge-recursive.h
mru.c [new file with mode: 0644]
mru.h [new file with mode: 0644]
pack-write.c
pager.c
refs.c
remote.c
remote.h
send-pack.c
sequencer.c
sha1_file.c
shallow.c
submodule-config.c
submodule-config.h
t/helper/test-run-command.c
t/lib-git-svn.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/perf/p5303-many-packs.sh [new file with mode: 0755]
t/t0006-date.sh
t/t3700-add.sh
t/t4014-format-patch.sh
t/t4130-apply-criss-cross-rename.sh
t/t4205-log-pretty-formats.sh
t/t4207-log-decoration-colors.sh
t/t5510-fetch.sh
t/t5520-pull.sh
t/t5533-push-cas.sh
t/t7006-pager.sh
t/t7063-status-untracked-cache.sh
t/t7406-submodule-update.sh
t/t7800-difftool.sh
t/t9100-git-svn-basic.sh
t/t9115-git-svn-dcommit-funky-renames.sh
t/t9118-git-svn-funky-branch-names.sh
t/t9120-git-svn-clone-with-percent-escapes.sh
t/t9142-git-svn-shallow-clone.sh
t/t9158-git-svn-mergeinfo.sh
t/t9160-git-svn-preserve-empty-dirs.sh
trailer.c
transport.c
wt-status.c
index a714e69a11f59576a24487d05f8422f792edd9f0..9441a54b0d5b89ea2c795f8f6ad8a99dfbe8b432 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -33,6 +33,7 @@ Cheng Renquan <crquan@gmail.com>
 Chris Shoemaker <c.shoemaker@cox.net>
 Chris Wright <chrisw@sous-sol.org> <chrisw@osdl.org>
 Cord Seele <cowose@gmail.com> <cowose@googlemail.com>
+Christian Couder <chriscool@tuxfamily.org> <christian.couder@gmail.com>
 Christian Stimming <stimming@tuhh.de> <chs@ckiste.goetheallee>
 Csaba Henk <csaba@gluster.com> <csaba@lowlife.hu>
 Dan Johnson <computerdruid@gmail.com>
index a9fb0736d65068db0d093faf950d98af6ce74d1e..d9ac291f5fb9c2b0193cf0086c4c3287a7b79a4d 100644 (file)
@@ -97,7 +97,6 @@ UI, Workflows & Features
  * "git status" learned to suggest "merge --abort" during a conflicted
    merge, just like it already suggests "rebase --abort" during a
    conflicted rebase.
-   (merge b0a61ab mm/status-suggest-merge-abort later to maint).
 
  * "git jump" script (in contrib/) has been updated a bit.
    (merge a91e692 jk/git-jump later to maint).
@@ -105,6 +104,18 @@ UI, Workflows & Features
  * "git push" and "git clone" learned to give better progress meters
    to the end user who is waiting on the terminal.
 
+ * An entry "git log --decorate" for the tip of the current branch is
+   shown as "HEAD -> name" (where "name" is the name of the branch);
+   paint the arrow in the same color as "HEAD", not in the color for
+   commits.
+
+ * "git format-patch" learned format.from configuration variable to
+   specify the default settings for its "--from" option.
+
+ * "git am -3" calls "git merge-recursive" when it needs to fall back
+   to a three-way merge; this call has been turned into an internal
+   subroutine call instead of spawning a separate subprocess.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -180,7 +191,6 @@ Performance, Internal Implementation, Development Support etc.
 
  * The .c/.h sources are marked as such in our .gitattributes file so
    that "git diff -W" and friends would work better.
-   (merge e82675a rs/help-c-source-with-gitattributes later to maint).
 
  * Code clean-up to avoid using a variable string that compilers may
    feel untrustable as printf-style format given to write_file()
@@ -193,7 +203,6 @@ Performance, Internal Implementation, Development Support etc.
    library did not check all the functions from pthread libraries;
    recent FreeBSD has some functions in libc but not others, and we
    mistakenly thought linking with libc is enough when it is not.
-   (merge a9b02de ew/autoconf-pthread later to maint).
 
  * When "git fsck" reports a broken link (e.g. a tree object contains
    a blob that does not exist), both containing object and the object
@@ -202,7 +211,6 @@ Performance, Internal Implementation, Development Support etc.
    the containing object from existing refs (e.g. "HEAD~24^2:file.txt").
 
  * Allow http daemon tests in Travis CI tests.
-   (merge d9d1426 ls/travis-enable-httpd-tests later to maint).
 
  * Makefile assumed that -lrt is always available on platforms that
    want to use clock_gettime() and CLOCK_MONOTONIC, which is not a
@@ -236,7 +244,6 @@ Performance, Internal Implementation, Development Support etc.
    to decide the set of supported options dynamically, which makes the
    code error-prone and hard to read.  This has been corrected by tweaking
    the API to allocate and return a new copy of "struct option" array.
-   (merge 023ff39 jk/parse-options-concat later to maint).
 
  * "git fetch" exchanges batched have/ack messages between the sender
    and the receiver, initially doubling every time and then falling
@@ -246,6 +253,27 @@ Performance, Internal Implementation, Development Support etc.
    repository.  The internal mechanism learned to grow the window size
    more aggressively when working with the "smart http" transport.
 
+ * Tests for "git svn" have been taught to reuse the lib-httpd test
+   infrastructure when testing the subversion integration that
+   interacts with subversion repositories served over the http://
+   protocol.
+   (merge a8a5d25 ew/git-svn-http-tests later to maint).
+
+ * "git pack-objects" has a few options that tell it not to pack
+   objects found in certain packfiles, which require it to scan .idx
+   files of all available packs.  The codepaths involved in these
+   operations have been optimized for a common case of not having any
+   non-local pack and/or any .kept pack.
+
+ * The t3700 test about "add --chmod=-x" have been made a bit more
+   robust and generally cleaned up.
+   (merge 766cdc4 ib/t3700-add-chmod-x-updates later to maint).
+
+ * The build procedure learned PAGER_ENV knob that lists what default
+   environment variable settings to export for popular pagers.  This
+   mechanism is used to tweak the default settings to MORE on FreeBSD.
+   (merge 995bc22 ew/build-time-pager-tweaks later to maint).
+
 
 Also contains various documentation updates and code clean-ups.
 
@@ -416,16 +444,13 @@ notes for details).
    "file" did not appear in the current commit.  When "file" was
    created by renaming an existing file (but the change has not been
    committed), this restriction was unnecessarily tight.
-   (merge c66b470 mh/blame-worktree later to maint).
 
  * "git add -N dir/file && git write-tree" produced an incorrect tree
    when there are other paths in the same directory that sorts after
    "file".
-   (merge 6d6a782 nd/cache-tree-ita later to maint).
 
  * "git fetch http://user:pass@host/repo..." scrubbed the userinfo
    part, but "git push" didn't.
-   (merge 68f3c07 jk/push-scrub-url later to maint).
 
  * "git merge" with renormalization did not work well with
    merge-recursive, due to "safer crlf" conversion kicking in when it
@@ -434,50 +459,41 @@ notes for details).
 
  * The use of strbuf in "git rm" to build filename to remove was a bit
    suboptimal, which has been fixed.
-   (merge deb8e15 rs/rm-strbuf-optim later to maint).
 
  * An age old bug that caused "git diff --ignore-space-at-eol"
    misbehave has been fixed.
-   (merge 044fb19 js/ignore-space-at-eol later to maint).
 
  * "git notes merge" had a code to see if a path exists (and fails if
    it does) and then open the path for writing (when it doesn't).
    Replace it with open with O_EXCL.
-   (merge deb9c15 rs/notes-merge-no-toctou later to maint).
 
  * "git pack-objects" and "git index-pack" mostly operate with off_t
    when talking about the offset of objects in a packfile, but there
    were a handful of places that used "unsigned long" to hold that
    value, leading to an unintended truncation.
-   (merge ec9d224 nd/pack-ofs-4gb-limit later to maint).
 
  * Recent update to "git daemon" tries to enable the socket-level
    KEEPALIVE, but when it is spawned via inetd, the standard input
    file descriptor may not necessarily be connected to a socket.
    Suppress an ENOTSOCK error from setsockopt().
-   (merge fab6027 ew/daemon-socket-keepalive later to maint).
 
  * Recent FreeBSD stopped making perl available at /usr/bin/perl;
    switch the default the built-in path to /usr/local/bin/perl on not
    too ancient FreeBSD releases.
-   (merge 259f22a ew/find-perl-on-freebsd-in-local later to maint).
 
  * "git commit --help" said "--no-verify" is only about skipping the
    pre-commit hook, and failed to say that it also skipped the
    commit-msg hook.
-   (merge def480f os/no-verify-skips-commit-msg-too later to maint).
 
  * "git merge" in Git v2.9 was taught to forbid merging an unrelated
    lines of history by default, but that is exactly the kind of thing
    the "--rejoin" mode of "git subtree" (in contrib/) wants to do.
    "git subtree" has been taught to use the "--allow-unrelated-histories"
    option to override the default.
-   (merge 0f12c7d da/subtree-2.9-regression later to maint).
 
  * The build procedure for "git persistent-https" helper (in contrib/)
    has been updated so that it can be built with more recent versions
    of Go.
-   (merge accb613 pm/build-persistent-https-with-recent-go later to maint).
 
  * There is an optimization used in "git diff $treeA $treeB" to borrow
    an already checked-out copy in the working tree when it is known to
@@ -488,23 +504,53 @@ notes for details).
    conversion (including the clean filter), which defeats the whole
    point of the optimization.  The optimization has been disabled when
    the conversion is necessary.
-   (merge 06dec43 jk/diff-do-not-reuse-wtf-needs-cleaning later to maint).
 
  * "git -c grep.patternType=extended log --basic-regexp" misbehaved
    because the internal API to access the grep machinery was not
    designed well.
-   (merge 8465541 jc/grep-commandline-vs-configuration later to maint).
+
+ * Windows port was failing some tests in t4130, due to the lack of
+   inum in the returned values by its lstat(2) emulation.
+
+ * The reflog output format is documented better, and a new format
+   --date=unix to report the seconds-since-epoch (without timezone)
+   has been added.
+   (merge 442f6fd jk/reflog-date later to maint).
+
+ * "git difftool <paths>..." started in a subdirectory failed to
+   interpret the paths relative to that directory, which has been
+   fixed.
+   (merge 32b8c58 jk/difftool-in-subdir later to maint).
+
+ * The characters in the label shown for tags/refs for commits in
+   "gitweb" output are now properly escaped for proper HTML output.
+
+ * FreeBSD can lie when asked mtime of a directory, which made the
+   untracked cache code to fall back to a slow-path, which in turn
+   caused tests in t7063 to fail because it wanted to verify the
+   behaviour of the fast-path.
+
+ * Squelch compiler warnings for netmalloc (in compat/) library.
+
+ * A small memory leak in the command line parsing of "git blame"
+   has been plugged.
+
+ * The API documentation for hashmap was unclear if hashmap_entry
+   can be safely discarded without any other consideration.  State
+   that it is safe to do so.
+
+ * Not-so-recent rewrite of "git am" that started making internal
+   calls into the commit machinery had an unintended regression, in
+   that no matter how many seconds it took to apply many patches, the
+   resulting committer timestamp for the resulting commits were all
+   the same.
+   (merge 4d9c7e6 jk/reset-ident-time-per-commit later to maint).
+
+ * "git push --force-with-lease" already had enough logic to allow
+   ensuring that such a push results in creation of a ref (i.e. the
+   receiving end did not have another push from sideways that would be
+   discarded by our force-pushing), but didn't expose this possibility
+   to the users.  It does so now.
+   (merge 9eed4f3 jk/push-force-with-lease-creation later to maint).
 
  * Other minor clean-ups and documentation updates
-   (merge e51b0df pb/commit-editmsg-path later to maint).
-   (merge b333d0d jk/send-pack-stdio later to maint).
-   (merge fcf0fe9 lf/sideband-returns-void later to maint).
-   (merge c2691e2 ah/unpack-trees-advice-messages later to maint).
-   (merge c61b2af lf/recv-sideband-cleanup later to maint).
-   (merge 31471ba rs/use-strbuf-addbuf later to maint).
-   (merge 503e224 nd/test-helpers later to maint).
-   (merge 16726cf jc/doc-diff-filter-exclude later to maint).
-   (merge fd2e7da rs/worktree-use-strbuf-absolute-path later to maint).
-   (merge 406621f sb/submodule-deinit-all later to maint).
-   (merge 55cbe18 rs/submodule-config-code-cleanup later to maint).
-   (merge 280abfd sb/pack-protocol-doc-nak later to maint).
index 28003a54ef1b499027c545b471258c43db3adfdf..d19f14438243e8229f9c2cfe53ef89b9ac059548 100644 (file)
@@ -55,4 +55,106 @@ Fixes since v2.9.2
  * A test that unconditionally used "mktemp" learned that the command
    is not necessarily available everywhere.
 
+ * "git blame file" allowed the lineage of lines in the uncommitted,
+   unadded contents of "file" to be inspected, but it refused when
+   "file" did not appear in the current commit.  When "file" was
+   created by renaming an existing file (but the change has not been
+   committed), this restriction was unnecessarily tight.
+
+ * "git add -N dir/file && git write-tree" produced an incorrect tree
+   when there are other paths in the same directory that sorts after
+   "file".
+
+ * "git fetch http://user:pass@host/repo..." scrubbed the userinfo
+   part, but "git push" didn't.
+
+ * An age old bug that caused "git diff --ignore-space-at-eol"
+   misbehave has been fixed.
+
+ * "git notes merge" had a code to see if a path exists (and fails if
+   it does) and then open the path for writing (when it doesn't).
+   Replace it with open with O_EXCL.
+
+ * "git pack-objects" and "git index-pack" mostly operate with off_t
+   when talking about the offset of objects in a packfile, but there
+   were a handful of places that used "unsigned long" to hold that
+   value, leading to an unintended truncation.
+
+ * Recent update to "git daemon" tries to enable the socket-level
+   KEEPALIVE, but when it is spawned via inetd, the standard input
+   file descriptor may not necessarily be connected to a socket.
+   Suppress an ENOTSOCK error from setsockopt().
+
+ * Recent FreeBSD stopped making perl available at /usr/bin/perl;
+   switch the default the built-in path to /usr/local/bin/perl on not
+   too ancient FreeBSD releases.
+
+ * "git status" learned to suggest "merge --abort" during a conflicted
+   merge, just like it already suggests "rebase --abort" during a
+   conflicted rebase.
+
+ * The .c/.h sources are marked as such in our .gitattributes file so
+   that "git diff -W" and friends would work better.
+
+ * Existing autoconf generated test for the need to link with pthread
+   library did not check all the functions from pthread libraries;
+   recent FreeBSD has some functions in libc but not others, and we
+   mistakenly thought linking with libc is enough when it is not.
+
+ * Allow http daemon tests in Travis CI tests.
+
+ * Users of the parse_options_concat() API function need to allocate
+   extra slots in advance and fill them with OPT_END() when they want
+   to decide the set of supported options dynamically, which makes the
+   code error-prone and hard to read.  This has been corrected by tweaking
+   the API to allocate and return a new copy of "struct option" array.
+
+ * The use of strbuf in "git rm" to build filename to remove was a bit
+   suboptimal, which has been fixed.
+
+ * "git commit --help" said "--no-verify" is only about skipping the
+   pre-commit hook, and failed to say that it also skipped the
+   commit-msg hook.
+
+ * "git merge" in Git v2.9 was taught to forbid merging an unrelated
+   lines of history by default, but that is exactly the kind of thing
+   the "--rejoin" mode of "git subtree" (in contrib/) wants to do.
+   "git subtree" has been taught to use the "--allow-unrelated-histories"
+   option to override the default.
+
+ * The build procedure for "git persistent-https" helper (in contrib/)
+   has been updated so that it can be built with more recent versions
+   of Go.
+
+ * There is an optimization used in "git diff $treeA $treeB" to borrow
+   an already checked-out copy in the working tree when it is known to
+   be the same as the blob being compared, expecting that open/mmap of
+   such a file is faster than reading it from the object store, which
+   involves inflating and applying delta.  This however kicked in even
+   when the checked-out copy needs to go through the convert-to-git
+   conversion (including the clean filter), which defeats the whole
+   point of the optimization.  The optimization has been disabled when
+   the conversion is necessary.
+
+ * "git -c grep.patternType=extended log --basic-regexp" misbehaved
+   because the internal API to access the grep machinery was not
+   designed well.
+
+ * Windows port was failing some tests in t4130, due to the lack of
+   inum in the returned values by its lstat(2) emulation.
+
+ * The characters in the label shown for tags/refs for commits in
+   "gitweb" output are now properly escaped for proper HTML output.
+
+ * FreeBSD can lie when asked mtime of a directory, which made the
+   untracked cache code to fall back to a slow-path, which in turn
+   caused tests in t7063 to fail because it wanted to verify the
+   behaviour of the fast-path.
+
+ * Squelch compiler warnings for netmalloc (in compat/) library.
+
+ * The API documentation for hashmap was unclear if hashmap_entry
+   can be safely discarded without any other consideration.  State
+   that it is safe to do so.
+
 Also contains minor documentation updates and code clean-ups.
index bc1c433c4e0584a095f79196997426780521b377..0bcb6790d6efbb2a812bc90b6f64e323cb3ec393 100644 (file)
@@ -1253,6 +1253,16 @@ format.attach::
        value as the boundary.  See the --attach option in
        linkgit:git-format-patch[1].
 
+format.from::
+       Provides the default value for the `--from` option to format-patch.
+       Accepts a boolean value, or a name and email address.  If false,
+       format-patch defaults to `--no-from`, using commit authors directly in
+       the "From:" field of patch mails.  If true, format-patch defaults to
+       `--from`, using your committer identity in the "From:" field of patch
+       mails and including a "From:" field in the body of the patch mail if
+       different.  If set to a non-boolean value, format-patch uses that
+       value instead of your committer identity.  Defaults to false.
+
 format.numbered::
        A boolean which can enable or disable sequence numbers in patch
        subjects.  It defaults to "auto" which enables it only if there
index 19cdcd03417dfa7ff7c744686466af733c367945..8973510a41c1e31319dad85d4987b3f0dfd7d8ca 100644 (file)
@@ -104,8 +104,8 @@ base-name::
        out of memory with a large window, but still be able to take
        advantage of the large window for the smaller objects.  The
        size can be suffixed with "k", "m", or "g".
-       `--window-memory=0` makes memory usage unlimited, which is the
-       default.
+       `--window-memory=0` makes memory usage unlimited.  The default
+       is taken from the `pack.windowMemory` configuration variable.
 
 --max-pack-size=<n>::
        Maximum size of each output pack file. The size can be suffixed with
index ec514f6cd5cc0f019b469d91fc6af365ce0a64a9..47b77e693bca1675a4887bddb5147077d60805a0 100644 (file)
@@ -204,10 +204,11 @@ branch we have for it.
 +
 `--force-with-lease=<refname>:<expect>` will protect the named ref (alone),
 if it is going to be updated, by requiring its current value to be
-the same as the specified value <expect> (which is allowed to be
+the same as the specified value `<expect>` (which is allowed to be
 different from the remote-tracking branch we have for the refname,
 or we do not even have to have such a remote-tracking branch when
-this form is used).
+this form is used).  If `<expect>` is the empty string, then the named ref
+must not already exist.
 +
 Note that all forms other than `--force-with-lease=<refname>:<expect>`
 that specifies the expected current value of the ref explicitly are
index c5975234f4752147c0149f67375ecd577599b4c1..26afe6ed549002f83296e76d8be6502332bc7edf 100644 (file)
@@ -100,8 +100,10 @@ other objects in that pack they already have locally.
        out of memory with a large window, but still be able to take
        advantage of the large window for the smaller objects.  The
        size can be suffixed with "k", "m", or "g".
-       `--window-memory=0` makes memory usage unlimited, which is the
-       default.
+       `--window-memory=0` makes memory usage unlimited.  The default
+       is taken from the `pack.windowMemory` configuration variable.
+       Note that the actual memory usage will be the limit multiplied
+       by the number of threads used by linkgit:git-pack-objects[1].
 
 --max-pack-size=<n>::
        Maximum size of each output pack file. The size can be suffixed with
index b40068bdfd14767fbfa659576f434944f4225b5e..34db3e219b285f13f8435ebd5307c2185fec15bf 100644 (file)
@@ -377,6 +377,11 @@ substitution.  For example:
        smudge = git-p4-filter --smudge %f
 ------------------------
 
+Note that "%f" is the name of the path that is being worked on. Depending
+on the version that is being filtered, the corresponding file on disk may
+not exist, or may have different contents. So, smudge and clean commands
+should not try to access the file on disk, but only act as filters on the
+content provided to them on standard input.
 
 Interaction between checkin/checkout attributes
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 07cdd73ab2e68da4aaef30a523801f56610b176c..0849d281bb79b8a394a00f50fc09a10aba15d4f2 100644 (file)
@@ -79,6 +79,11 @@ submodule.<name>.ignore::
        "--ignore-submodule" option. The 'git submodule' commands are not
        affected by this setting.
 
+submodule.<name>.shallow::
+       When set to true, a clone of this submodule will be performed as a
+       shallow clone unless the user explicitely asks for a non-shallow
+       clone.
+
 
 EXAMPLES
 --------
index 29b19b992f2e652d6fc9258cbd64cad51f86c2ef..b95d67ec01f130417af9ff0d7bd0d2377008146c 100644 (file)
@@ -147,8 +147,14 @@ endif::git-rev-list[]
   "U" for a good signature with unknown validity and "N" for no signature
 - '%GS': show the name of the signer for a signed commit
 - '%GK': show the key used to sign a signed commit
-- '%gD': reflog selector, e.g., `refs/stash@{1}`
-- '%gd': shortened reflog selector, e.g., `stash@{1}`
+- '%gD': reflog selector, e.g., `refs/stash@{1}` or
+  `refs/stash@{2 minutes ago`}; the format follows the rules described
+  for the `-g` option. The portion before the `@` is the refname as
+  given on the command line (so `git log -g refs/heads/master` would
+  yield `refs/heads/master@{0}`).
+- '%gd': shortened reflog selector; same as `%gD`, but the refname
+  portion is shortened for human readability (so `refs/heads/master`
+  becomes just `master`).
 - '%gn': reflog identity name
 - '%gN': reflog identity name (respecting .mailmap, see
   linkgit:git-shortlog[1] or linkgit:git-blame[1])
index f39cb6d4f56e55da0c38841c9da0346b12b841a0..a779c9dfec0ed962e7274577b1c53daa0ecff1d9 100644 (file)
@@ -252,10 +252,25 @@ list.
 +
 With `--pretty` format other than `oneline` (for obvious reasons),
 this causes the output to have two extra lines of information
-taken from the reflog.  By default, 'commit@\{Nth}' notation is
-used in the output.  When the starting commit is specified as
-'commit@\{now}', output also uses 'commit@\{timestamp}' notation
-instead.  Under `--pretty=oneline`, the commit message is
+taken from the reflog.  The reflog designator in the output may be shown
+as `ref@{Nth}` (where `Nth` is the reverse-chronological index in the
+reflog) or as `ref@{timestamp}` (with the timestamp for that entry),
+depending on a few rules:
++
+--
+1. If the starting point is specified as `ref@{Nth}`, show the index
+format.
++
+2. If the starting point was specified as `ref@{now}`, show the
+timestamp format.
++
+3. If neither was used, but `--date` was given on the command line, show
+the timestamp in the format requested by `--date`.
++
+4. Otherwise, show the index format.
+--
++
+Under `--pretty=oneline`, the commit message is
 prefixed with this information on the same line.
 This option cannot be combined with `--reverse`.
 See also linkgit:git-reflog[1].
@@ -714,8 +729,8 @@ include::pretty-options.txt[]
        `iso-local`), the user's local time zone is used instead.
 +
 `--date=relative` shows dates relative to the current time,
-e.g. ``2 hours ago''. The `-local` option cannot be used with
-`--raw` or `--relative`.
+e.g. ``2 hours ago''. The `-local` option has no effect for
+`--date=relative`.
 +
 `--date=local` is an alias for `--date=default-local`.
 +
@@ -735,7 +750,18 @@ format, often found in email messages.
 +
 `--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.
 +
-`--date=raw` shows the date in the internal raw Git format `%s %z` format.
+`--date=raw` shows the date as seconds since the epoch (1970-01-01
+00:00:00 UTC), followed by a space, and then the timezone as an offset
+from UTC (a `+` or `-` with four digits; the first two are hours, and
+the second two are minutes). I.e., as if the timestamp were formatted
+with `strftime("%s %z")`).
+Note that the `-local` option does not affect the seconds-since-epoch
+value (which is always measured in UTC), but does switch the accompanying
+timezone value.
++
+`--date=unix` shows the date as a Unix epoch timestamp (seconds since
+1970).  As with `--raw`, this is always in UTC and therefore `-local`
+has no effect.
 +
 `--date=format:...` feeds the format `...` to your system `strftime`.
 Use `--date=format:%c` to show the date in your system locale's
index ad7a5bddd24d91ceda78d430fd86a204b21fd005..28f5a8b71574916820cdb1f1a023e27cb45ed6e6 100644 (file)
@@ -104,6 +104,11 @@ If `free_entries` is true, each hashmap_entry in the map is freed as well
 `entry` points to the entry to initialize.
 +
 `hash` is the hash code of the entry.
++
+The hashmap_entry structure does not hold references to external resources,
+and it is safe to just discard it once you are done with it (i.e. if
+your structure was allocated with xmalloc(), you can just free(3) it,
+and if it is on stack, you can just let it go out of scope).
 
 `void *hashmap_get(const struct hashmap *map, const void *key, const void *keydata)`::
 
index 6a13386c27336b0f3054389674872a2c842f3290..d96ecb7141a12f1fe47246805c5d4e4e562a5c86 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -370,6 +370,14 @@ all::
 # Define HAVE_BSD_SYSCTL if your platform has a BSD-compatible sysctl function.
 #
 # Define HAVE_GETDELIM if your system has the getdelim() function.
+#
+# Define PAGER_ENV to a SP separated VAR=VAL pairs to define
+# default environment variables to be passed when a pager is spawned, e.g.
+#
+#    PAGER_ENV = LESS=FRX LV=-c
+#
+# to say "export LESS=FRX (and LV=-c) if the environment variable
+# LESS (and LV) is not set, respectively".
 
 GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -755,6 +763,7 @@ LIB_OBJS += merge.o
 LIB_OBJS += merge-blobs.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += mergesort.o
+LIB_OBJS += mru.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
@@ -1500,6 +1509,10 @@ ifeq ($(PYTHON_PATH),)
 NO_PYTHON = NoThanks
 endif
 
+ifndef PAGER_ENV
+PAGER_ENV = LESS=FRX LV=-c
+endif
+
 QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
 QUIET_SUBDIR1  =
 
@@ -1629,6 +1642,11 @@ ifdef DEFAULT_HELP_FORMAT
 BASIC_CFLAGS += -DDEFAULT_HELP_FORMAT='"$(DEFAULT_HELP_FORMAT)"'
 endif
 
+PAGER_ENV_SQ = $(subst ','\'',$(PAGER_ENV))
+PAGER_ENV_CQ = "$(subst ",\",$(subst \,\\,$(PAGER_ENV)))"
+PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ))
+BASIC_CFLAGS += -DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
+
 ALL_CFLAGS += $(BASIC_CFLAGS)
 ALL_LDFLAGS += $(BASIC_LDFLAGS)
 
@@ -1753,7 +1771,7 @@ common-cmds.h: $(wildcard Documentation/git-*.txt)
 
 SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
        $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
-       $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP)
+       $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV)
 define cmd_munge_script
 $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
@@ -1766,6 +1784,7 @@ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
     -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
     -e 's|@@SANE_TEXT_GREP@@|$(SANE_TEXT_GREP)|g' \
+    -e 's|@@PAGER_ENV@@|$(PAGER_ENV_SQ)|g' \
     $@.sh >$@+
 endef
 
@@ -2173,6 +2192,7 @@ GIT-BUILD-OPTIONS: FORCE
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
        @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
+       @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
 ifdef TEST_OUTPUT_DIRECTORY
        @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
 endif
index b77bf11acecd7cd80171c78378154c712415af6f..739b34dcf25a1355246bed436ec7fdb4723d7ca7 100644 (file)
@@ -1578,48 +1578,19 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f
        return 0;
 }
 
-/**
- * Do the three-way merge using fake ancestor, their tree constructed
- * from the fake ancestor and the postimage of the patch, and our
- * state.
- */
-static int run_fallback_merge_recursive(const struct am_state *state,
-                                       unsigned char *orig_tree,
-                                       unsigned char *our_tree,
-                                       unsigned char *their_tree)
-{
-       struct child_process cp = CHILD_PROCESS_INIT;
-       int status;
-
-       cp.git_cmd = 1;
-
-       argv_array_pushf(&cp.env_array, "GITHEAD_%s=%.*s",
-                        sha1_to_hex(their_tree), linelen(state->msg), state->msg);
-       if (state->quiet)
-               argv_array_push(&cp.env_array, "GIT_MERGE_VERBOSITY=0");
-
-       argv_array_push(&cp.args, "merge-recursive");
-       argv_array_push(&cp.args, sha1_to_hex(orig_tree));
-       argv_array_push(&cp.args, "--");
-       argv_array_push(&cp.args, sha1_to_hex(our_tree));
-       argv_array_push(&cp.args, sha1_to_hex(their_tree));
-
-       status = run_command(&cp) ? (-1) : 0;
-       discard_cache();
-       read_cache();
-       return status;
-}
-
 /**
  * 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], their_tree[GIT_SHA1_RAWSZ],
-                     our_tree[GIT_SHA1_RAWSZ];
+       struct object_id orig_tree, their_tree, our_tree;
+       const struct object_id *bases[1] = { &orig_tree };
+       struct merge_options o;
+       struct commit *result;
+       char *their_tree_name;
 
-       if (get_sha1("HEAD", our_tree) < 0)
-               hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
+       if (get_oid("HEAD", &our_tree) < 0)
+               hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);
 
        if (build_fake_ancestor(state, index_path))
                return error("could not build fake ancestor");
@@ -1627,7 +1598,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
        discard_cache();
        read_cache_from(index_path);
 
-       if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))
+       if (write_index_as_tree(orig_tree.hash, &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..."));
@@ -1643,7 +1614,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
                init_revisions(&rev_info, NULL);
                rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
                diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
-               add_pending_sha1(&rev_info, "HEAD", our_tree, 0);
+               add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0);
                diff_setup_done(&rev_info.diffopt);
                run_diff_index(&rev_info, 1);
        }
@@ -1652,7 +1623,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
                return error(_("Did you hand edit your patch?\n"
                                "It does not apply to blobs recorded in its index."));
 
-       if (write_index_as_tree(their_tree, &the_index, index_path, 0, NULL))
+       if (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))
                return error("could not write tree");
 
        say(state, stdout, _("Falling back to patching base and 3-way merge..."));
@@ -1668,11 +1639,22 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
         * changes.
         */
 
-       if (run_fallback_merge_recursive(state, orig_tree, our_tree, their_tree)) {
+       init_merge_options(&o);
+
+       o.branch1 = "HEAD";
+       their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
+       o.branch2 = their_tree_name;
+
+       if (state->quiet)
+               o.verbosity = 0;
+
+       if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
                rerere(state->allow_rerere_autoupdate);
+               free(their_tree_name);
                return error(_("Failed to merge in the changes."));
        }
 
+       free(their_tree_name);
        return 0;
 }
 
@@ -1840,6 +1822,8 @@ static void am_run(struct am_state *state, int resume)
                const char *mail = am_path(state, msgnum(state));
                int apply_status;
 
+               reset_ident_date();
+
                if (!file_exists(mail))
                        goto next;
 
index ab66cde2c2cf0f535678469cdf6eb6e37cd3113b..7ec782343002d27b1b7fdd7e61fada12e81eeaf1 100644 (file)
@@ -2633,6 +2633,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        case DATE_RAW:
                blame_date_width = sizeof("1161298804 -0700");
                break;
+       case DATE_UNIX:
+               blame_date_width = sizeof("1161298804");
+               break;
        case DATE_SHORT:
                blame_date_width = sizeof("2006-10-19");
                break;
@@ -2805,7 +2808,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        lno = prepare_lines(&sb);
 
        if (lno && !range_list.nr)
-               string_list_append(&range_list, xstrdup("1"));
+               string_list_append(&range_list, "1");
 
        anchor = 1;
        range_set_init(&ranges, range_list.nr);
index 27c1a05246c21278a730698bd7de85eccc96d721..8d852d458514ca8d2e9553bec6a818b140b0e478 100644 (file)
@@ -567,10 +567,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        o.ancestor = old->name;
                        o.branch1 = new->name;
                        o.branch2 = "local";
-                       merge_trees(&o, new->commit->tree, work,
+                       ret = merge_trees(&o, new->commit->tree, work,
                                old->commit->tree, &result);
+                       if (ret < 0)
+                               exit(128);
                        ret = reset_tree(new->commit->tree, opts, 0,
                                         writeout_error);
+                       strbuf_release(&o.obuf);
                        if (ret)
                                return ret;
                }
index a991a53418506f18f439212f8cfe1f6f994ea571..6cbf73369b2f6cf41430d78e0a5e8ffa18de6d15 100644 (file)
@@ -25,7 +25,6 @@ static char term = '\n';
 static int use_global_config, use_system_config, use_local_config;
 static struct git_config_source given_config_source;
 static int actions, types;
-static const char *get_color_slot, *get_colorbool_slot;
 static int end_null;
 static int respect_includes = -1;
 static int show_origin;
index e5658c320ee45c208d4087e2b9761f38e0aeca92..ac84e99f3a6c073409ab7514aacdad7e722c6255 100644 (file)
@@ -272,7 +272,7 @@ static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
 static void add_people_count(struct strbuf *out, struct string_list *people)
 {
        if (people->nr == 1)
-               strbuf_addf(out, "%s", people->items[0].string);
+               strbuf_addstr(out, people->items[0].string);
        else if (people->nr == 2)
                strbuf_addf(out, "%s (%d) and %s (%d)",
                            people->items[0].string,
index fd1652f52b32fc8bfb7bdc6e0191affafaa21c9c..1f116bea8c8873d652f573db2047c558fa51bbd3 100644 (file)
@@ -719,6 +719,7 @@ static void add_header(const char *value)
 static int thread;
 static int do_signoff;
 static int base_auto;
+static char *from;
 static const char *signature = git_version_string;
 static const char *signature_file;
 static int config_cover_letter;
@@ -807,6 +808,17 @@ static int git_format_config(const char *var, const char *value, void *cb)
                base_auto = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "format.from")) {
+               int b = git_config_maybe_bool(var, value);
+               free(from);
+               if (b < 0)
+                       from = xstrdup(value);
+               else if (b)
+                       from = xstrdup(git_committer_info(IDENT_NO_DATE));
+               else
+                       from = NULL;
+               return 0;
+       }
 
        return git_log_config(var, value, cb);
 }
@@ -1384,7 +1396,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        int reroll_count = -1;
        char *branch_name = NULL;
-       char *from = NULL;
        char *base_commit = NULL;
        struct base_tree_info bases;
 
index f02e3d23bb71a39dfd9f6eddf40aa84da896b650..00ea91aae6325fccafc22c64d86c5f7335851b81 100644 (file)
@@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)
                                 */
                                pos = cache_name_pos(ent->name, ent->len);
                                if (0 <= pos)
-                                       die("bug in show-killed-files");
+                                       die("BUG: killed-file %.*s not found",
+                                               ent->len, ent->name);
                                pos = -pos - 1;
                                while (pos < active_nr &&
                                       ce_stage(active_cache[pos]))
index 19b3bc2f2fafe08d5ec03ab493d9d8f189db275b..148a9a51b9eb45b0b5113680aaf6a71c5db60e09 100644 (file)
@@ -673,6 +673,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                hold_locked_index(&lock, 1);
                clean = merge_recursive(&o, head,
                                remoteheads->item, reversed, &result);
+               if (clean < 0)
+                       exit(128);
                if (active_cache_changed &&
                    write_locked_index(&the_index, &lock, COMMIT_LOCK))
                        die (_("unable to write %s"), get_index_file());
index 05720517628270669d9e231c2030befd2d6325f6..f848b896927a34e884f8377ea9c98271237be5fc 100644 (file)
@@ -91,7 +91,7 @@ static const char * const git_notes_get_ref_usage[] = {
 };
 
 static const char note_template[] =
-       "\nWrite/edit the notes for the following object:\n";
+       N_("Write/edit the notes for the following object:");
 
 struct note_data {
        int given;
@@ -179,7 +179,8 @@ static void prepare_note_data(const unsigned char *object, struct note_data *d,
                        copy_obj_to_fd(fd, old_note);
 
                strbuf_addch(&buf, '\n');
-               strbuf_add_commented_lines(&buf, note_template, strlen(note_template));
+               strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
+               strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)));
                strbuf_addch(&buf, '\n');
                write_or_die(fd, buf.buf, buf.len);
 
index 92e2e5f7a8190c546f2c0cc9bc9724a04d330052..4a63398960c42d91821c4278a4cfbd11c1f1a7a4 100644 (file)
@@ -46,6 +46,7 @@ static int keep_unreachable, unpack_unreachable, include_tag;
 static unsigned long unpack_unreachable_expiration;
 static int pack_loose_unreachable;
 static int local;
+static int have_non_local_packs;
 static int incremental;
 static int ignore_packed_keep;
 static int allow_ofs_delta;
@@ -978,6 +979,23 @@ static int want_object_in_pack(const unsigned char *sha1,
                                return 1;
                        if (incremental)
                                return 0;
+
+                       /*
+                        * When asked to do --local (do not include an
+                        * object that appears in a pack we borrow
+                        * from elsewhere) or --honor-pack-keep (do not
+                        * include an object that appears in a pack marked
+                        * with .keep), we need to make sure no copy of this
+                        * object come from in _any_ pack that causes us to
+                        * omit it, and need to complete this loop.  When
+                        * neither option is in effect, we know the object
+                        * we just found is going to be packed, so break
+                        * out of the loop to return 1 now.
+                        */
+                       if (!ignore_packed_keep &&
+                           (!local || !have_non_local_packs))
+                               break;
+
                        if (local && !p->pack_local)
                                return 0;
                        if (ignore_packed_keep && p->pack_local && p->pack_keep)
@@ -2784,6 +2802,28 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                progress = 2;
 
        prepare_packed_git();
+       if (ignore_packed_keep) {
+               struct packed_git *p;
+               for (p = packed_git; p; p = p->next)
+                       if (p->pack_local && p->pack_keep)
+                               break;
+               if (!p) /* no keep-able packs found */
+                       ignore_packed_keep = 0;
+       }
+       if (local) {
+               /*
+                * unlike ignore_packed_keep above, we do not want to
+                * unset "local" based on looking at packs, as it
+                * also covers non-local objects
+                */
+               struct packed_git *p;
+               for (p = packed_git; p; p = p->next) {
+                       if (!p->pack_local) {
+                               have_non_local_packs = 1;
+                               break;
+                       }
+               }
+       }
 
        if (progress)
                progress_state = start_progress(_("Counting objects"), 0);
index c961b74c5aaae41153b89f4e877437ba7f0d70c7..76cf05e2ade4348b8ad59b1541b58f671e53e1ce 100644 (file)
@@ -469,7 +469,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
                        (stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
                        PARSE_OPT_SHELL_EVAL);
 
-       strbuf_addf(&parsed, " --");
+       strbuf_addstr(&parsed, " --");
        sq_quote_argv(&parsed, argv, 0);
        puts(parsed.buf);
        return 0;
index e3bd2461f4b242e40c2ec14d94ff913a89d5f160..e79790f0bdc9f19cb9d9773d1a9bdb10044bbb8d 100644 (file)
@@ -747,8 +747,12 @@ static int update_clone_get_next_task(struct child_process *child,
        if (index < suc->failed_clones_nr) {
                int *p;
                ce = suc->failed_clones[index];
-               if (!prepare_to_clone_next_submodule(ce, child, suc, err))
-                       die("BUG: ce was a submodule before?");
+               if (!prepare_to_clone_next_submodule(ce, child, suc, err)) {
+                       suc->current ++;
+                       strbuf_addf(err, "BUG: submodule considered for cloning,"
+                                   "doesn't need cloning any more?\n");
+                       return 0;
+               }
                p = xmalloc(sizeof(*p));
                *p = suc->current;
                *idx_task_cb = p;
@@ -891,13 +895,64 @@ static int resolve_relative_path(int argc, const char **argv, const char *prefix
 {
        struct strbuf sb = STRBUF_INIT;
        if (argc != 3)
-               die("submodule--helper relative_path takes exactly 2 arguments, got %d", argc);
+               die("submodule--helper relative-path takes exactly 2 arguments, got %d", argc);
 
        printf("%s", relative_path(argv[1], argv[2], &sb));
        strbuf_release(&sb);
        return 0;
 }
 
+static const char *remote_submodule_branch(const char *path)
+{
+       const struct submodule *sub;
+       gitmodules_config();
+       git_config(submodule_config, NULL);
+
+       sub = submodule_from_path(null_sha1, path);
+       if (!sub)
+               return NULL;
+
+       if (!sub->branch)
+               return "master";
+
+       if (!strcmp(sub->branch, ".")) {
+               unsigned char sha1[20];
+               const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+
+               if (!refname)
+                       die(_("No such ref: %s"), "HEAD");
+
+               /* detached HEAD */
+               if (!strcmp(refname, "HEAD"))
+                       die(_("Submodule (%s) branch configured to inherit "
+                             "branch from superproject, but the superproject "
+                             "is not on any branch"), sub->name);
+
+               if (!skip_prefix(refname, "refs/heads/", &refname))
+                       die(_("Expecting a full ref name, got %s"), refname);
+               return refname;
+       }
+
+       return sub->branch;
+}
+
+static int resolve_remote_submodule_branch(int argc, const char **argv,
+               const char *prefix)
+{
+       const char *ret;
+       struct strbuf sb = STRBUF_INIT;
+       if (argc != 2)
+               die("submodule--helper remote-branch takes exactly one arguments, got %d", argc);
+
+       ret = remote_submodule_branch(argv[1]);
+       if (!ret)
+               die("submodule %s doesn't exist", argv[1]);
+
+       printf("%s", ret);
+       strbuf_release(&sb);
+       return 0;
+}
+
 struct cmd_struct {
        const char *cmd;
        int (*fn)(int, const char **, const char *);
@@ -911,7 +966,8 @@ static struct cmd_struct commands[] = {
        {"relative-path", resolve_relative_path},
        {"resolve-relative-url", resolve_relative_url},
        {"resolve-relative-url-test", resolve_relative_url_test},
-       {"init", module_init}
+       {"init", module_init},
+       {"remote-branch", resolve_remote_submodule_branch}
 };
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
index 6cdfd5f7308df9df10afd90748a83d7abb61ca65..ba04b197d8cc0b121475b1590952fe34ad1455d1 100644 (file)
@@ -1146,7 +1146,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
                break;
        default:
-               die("Bug: bad untracked_cache value: %d", untracked_cache);
+               die("BUG: bad untracked_cache value: %d", untracked_cache);
        }
 
        if (active_cache_changed) {
diff --git a/cache.h b/cache.h
index b5f76a4cf4b1599f024f14553dd8c99a63abf77c..95a0bd397a9858cc3ea19778112dcc1dd036642d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1230,7 +1230,8 @@ struct date_mode {
                DATE_ISO8601_STRICT,
                DATE_RFC2822,
                DATE_STRFTIME,
-               DATE_RAW
+               DATE_RAW,
+               DATE_UNIX
        } type;
        const char *strftime_fmt;
        int local;
@@ -1269,6 +1270,7 @@ extern const char *ident_default_email(void);
 extern const char *git_editor(void);
 extern const char *git_pager(int stdout_is_tty);
 extern int git_ident_config(const char *, const char *, void *);
+extern void reset_ident_date(void);
 
 struct ident_split {
        const char *name_begin;
@@ -1377,6 +1379,13 @@ extern struct packed_git {
        char pack_name[FLEX_ARRAY]; /* more */
 } *packed_git;
 
+/*
+ * A most-recently-used ordered version of the packed_git list, which can
+ * be iterated instead of packed_git (and marked via mru_mark).
+ */
+struct mru;
+extern struct mru *packed_git_mru;
+
 struct pack_entry {
        off_t offset;
        unsigned char sha1[20];
@@ -1416,7 +1425,6 @@ extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t
 extern void close_pack_windows(struct packed_git *);
 extern void close_all_packs(void);
 extern void unuse_pack(struct pack_window **);
-extern void free_pack_by_name(const char *);
 extern void clear_delta_base_cache(void);
 extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
 
@@ -1566,10 +1574,18 @@ struct git_config_source {
        const char *blob;
 };
 
+enum config_origin_type {
+       CONFIG_ORIGIN_BLOB,
+       CONFIG_ORIGIN_FILE,
+       CONFIG_ORIGIN_STDIN,
+       CONFIG_ORIGIN_SUBMODULE_BLOB,
+       CONFIG_ORIGIN_CMDLINE
+};
+
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
-extern int git_config_from_mem(config_fn_t fn, const char *origin_type,
+extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
                                        const char *name, const char *buf, size_t len, void *data);
 extern void git_config_push_parameter(const char *text);
 extern int git_config_from_parameters(config_fn_t fn, void *data);
@@ -1713,7 +1729,7 @@ extern int ignore_untracked_cache_config;
 struct key_value_info {
        const char *filename;
        int linenr;
-       const char *origin_type;
+       enum config_origin_type origin_type;
        enum config_scope scope;
 };
 
index f84b449413d434b8145426503a98bcd4f9ad995f..006a50b5481b552278931f5aec33883d79891a80 100644 (file)
@@ -126,16 +126,16 @@ static MAYBE_UNUSED elemtype *slabname## _peek(struct slabname *s,        \
        return slabname##_at_peek(s, c, 0);                             \
 }                                                                      \
                                                                        \
-static int stat_ ##slabname## realloc
+struct slabname
 
 /*
- * Note that this seemingly redundant second declaration is required
+ * Note that this redundant forward declaration is required
  * to allow a terminating semicolon, which makes instantiations look
  * like function declarations.  I.e., the expansion of
  *
  *    define_commit_slab(indegree, int);
  *
- * ends in 'static int stat_indegreerealloc;'.  This would otherwise
+ * ends in 'struct indegree;'.  This would otherwise
  * be a syntax error according (at least) to ISO C.  It's hard to
  * catch because GCC silently parses it by default.
  */
index a0a16eb1bbfb22c13f29c15d2fd68ccc9c7e01ad..2d4ef59013823d3c7caac8b705fbd4916cfe8fd6 100644 (file)
@@ -938,10 +938,10 @@ void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **
        void **ret;
        threadcache *tc;
        int mymspace;
-    size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
-    if(!adjustedsizes) return 0;
-    for(i=0; i<elems; i++)
-       adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
+       size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
+       if(!adjustedsizes) return 0;
+       for(i=0; i<elems; i++)
+               adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
        GetThreadCache(&p, &tc, &mymspace, 0);
        GETMSPACE(m, p, tc, mymspace, 0,
              ret=mspace_independent_comalloc(m, elems, adjustedsizes, chunks));
@@ -955,12 +955,11 @@ void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **
  */
 char *strdup(const char *s1)
 {
-       char *s2 = 0;
-       if (s1) {
-               size_t len = strlen(s1) + 1;
-               s2 = malloc(len);
+       size_t len = strlen(s1) + 1;
+       char *s2 = malloc(len);
+
+       if (s2)
                memcpy(s2, s1, len);
-       }
        return s2;
 }
 #endif
index bea937e4ecf8417a910882c1aafb263e792f2e1c..584cacf148336b90d9a5d87949b7594021ced42b 100644 (file)
--- a/config.c
+++ b/config.c
@@ -24,7 +24,7 @@ struct config_source {
                        size_t pos;
                } buf;
        } u;
-       const char *origin_type;
+       enum config_origin_type origin_type;
        const char *name;
        const char *path;
        int die_on_error;
@@ -245,6 +245,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
 
        memset(&source, 0, sizeof(source));
        source.prev = cf;
+       source.origin_type = CONFIG_ORIGIN_CMDLINE;
        cf = &source;
 
        /* sq_dequote will write over it */
@@ -453,6 +454,8 @@ static int git_parse_source(config_fn_t fn, void *data)
        int comment = 0;
        int baselen = 0;
        struct strbuf *var = &cf->var;
+       int error_return = 0;
+       char *error_msg = NULL;
 
        /* U+FEFF Byte Order Mark in UTF8 */
        const char *bomptr = utf8_bom;
@@ -507,10 +510,40 @@ static int git_parse_source(config_fn_t fn, void *data)
                if (get_value(fn, data, var) < 0)
                        break;
        }
+
+       switch (cf->origin_type) {
+       case CONFIG_ORIGIN_BLOB:
+               error_msg = xstrfmt(_("bad config line %d in blob %s"),
+                                     cf->linenr, cf->name);
+               break;
+       case CONFIG_ORIGIN_FILE:
+               error_msg = xstrfmt(_("bad config line %d in file %s"),
+                                     cf->linenr, cf->name);
+               break;
+       case CONFIG_ORIGIN_STDIN:
+               error_msg = xstrfmt(_("bad config line %d in standard input"),
+                                     cf->linenr);
+               break;
+       case CONFIG_ORIGIN_SUBMODULE_BLOB:
+               error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"),
+                                      cf->linenr, cf->name);
+               break;
+       case CONFIG_ORIGIN_CMDLINE:
+               error_msg = xstrfmt(_("bad config line %d in command line %s"),
+                                      cf->linenr, cf->name);
+               break;
+       default:
+               error_msg = xstrfmt(_("bad config line %d in %s"),
+                                     cf->linenr, cf->name);
+       }
+
        if (cf->die_on_error)
-               die(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
+               die("%s", error_msg);
        else
-               return error(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
+               error_return = error("%s", error_msg);
+
+       free(error_msg);
+       return error_return;
 }
 
 static int parse_unit_factor(const char *end, uintmax_t *val)
@@ -619,16 +652,47 @@ int git_parse_ulong(const char *value, unsigned long *ret)
 NORETURN
 static void die_bad_number(const char *name, const char *value)
 {
-       const char *reason = errno == ERANGE ?
-                            "out of range" :
-                            "invalid unit";
        if (!value)
                value = "";
 
-       if (cf && cf->origin_type && cf->name)
-               die(_("bad numeric config value '%s' for '%s' in %s %s: %s"),
-                   value, name, cf->origin_type, cf->name, reason);
-       die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason);
+       if (!(cf && cf->name))
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s': out of range")
+                   : _("bad numeric config value '%s' for '%s': invalid unit"),
+                   value, name);
+
+       switch (cf->origin_type) {
+       case CONFIG_ORIGIN_BLOB:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in blob %s: out of range")
+                   : _("bad numeric config value '%s' for '%s' in blob %s: invalid unit"),
+                   value, name, cf->name);
+       case CONFIG_ORIGIN_FILE:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in file %s: out of range")
+                   : _("bad numeric config value '%s' for '%s' in file %s: invalid unit"),
+                   value, name, cf->name);
+       case CONFIG_ORIGIN_STDIN:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in standard input: out of range")
+                   : _("bad numeric config value '%s' for '%s' in standard input: invalid unit"),
+                   value, name);
+       case CONFIG_ORIGIN_SUBMODULE_BLOB:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in submodule-blob %s: out of range")
+                   : _("bad numeric config value '%s' for '%s' in submodule-blob %s: invalid unit"),
+                   value, name, cf->name);
+       case CONFIG_ORIGIN_CMDLINE:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in command line %s: out of range")
+                   : _("bad numeric config value '%s' for '%s' in command line %s: invalid unit"),
+                   value, name, cf->name);
+       default:
+               die(errno == ERANGE
+                   ? _("bad numeric config value '%s' for '%s' in %s: out of range")
+                   : _("bad numeric config value '%s' for '%s' in %s: invalid unit"),
+                   value, name, cf->name);
+       }
 }
 
 int git_config_int(const char *name, const char *value)
@@ -1105,7 +1169,8 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
 }
 
 static int do_config_from_file(config_fn_t fn,
-               const char *origin_type, const char *name, const char *path, FILE *f,
+               const enum config_origin_type origin_type,
+               const char *name, const char *path, FILE *f,
                void *data)
 {
        struct config_source top;
@@ -1124,7 +1189,7 @@ static int do_config_from_file(config_fn_t fn,
 
 static int git_config_from_stdin(config_fn_t fn, void *data)
 {
-       return do_config_from_file(fn, "standard input", "", NULL, stdin, data);
+       return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, data);
 }
 
 int git_config_from_file(config_fn_t fn, const char *filename, void *data)
@@ -1135,14 +1200,14 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
        f = fopen(filename, "r");
        if (f) {
                flockfile(f);
-               ret = do_config_from_file(fn, "file", filename, filename, f, data);
+               ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data);
                funlockfile(f);
                fclose(f);
        }
        return ret;
 }
 
-int git_config_from_mem(config_fn_t fn, const char *origin_type,
+int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type,
                        const char *name, const char *buf, size_t len, void *data)
 {
        struct config_source top;
@@ -1179,7 +1244,7 @@ static int git_config_from_blob_sha1(config_fn_t fn,
                return error("reference '%s' does not point to a blob", name);
        }
 
-       ret = git_config_from_mem(fn, "blob", name, buf, size, data);
+       ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size, data);
        free(buf);
 
        return ret;
@@ -1390,12 +1455,12 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
        if (cf->name) {
                kv_info->filename = strintern(cf->name);
                kv_info->linenr = cf->linenr;
-               kv_info->origin_type = strintern(cf->origin_type);
+               kv_info->origin_type = cf->origin_type;
        } else {
                /* for values read from `git_config_from_parameters()` */
                kv_info->filename = NULL;
                kv_info->linenr = -1;
-               kv_info->origin_type = NULL;
+               kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
        }
        kv_info->scope = current_parsing_scope;
        si->util = kv_info;
@@ -2476,14 +2541,28 @@ int parse_config_key(const char *var,
 
 const char *current_config_origin_type(void)
 {
-       const char *type;
+       int type;
        if (current_config_kvi)
                type = current_config_kvi->origin_type;
        else if(cf)
                type = cf->origin_type;
        else
                die("BUG: current_config_origin_type called outside config callback");
-       return type ? type : "command line";
+
+       switch (type) {
+       case CONFIG_ORIGIN_BLOB:
+               return "blob";
+       case CONFIG_ORIGIN_FILE:
+               return "file";
+       case CONFIG_ORIGIN_STDIN:
+               return "standard input";
+       case CONFIG_ORIGIN_SUBMODULE_BLOB:
+               return "submodule-blob";
+       case CONFIG_ORIGIN_CMDLINE:
+               return "command line";
+       default:
+               die("BUG: unknown config origin type");
+       }
 }
 
 const char *current_config_name(void)
index 17fed2f43ac6f7564785e2074e7f130bd625c231..b232908f8c8c2eae84bd6ef8ab2a96ac45bf94a3 100644 (file)
@@ -209,6 +209,7 @@ ifeq ($(uname_S),FreeBSD)
        HAVE_PATHS_H = YesPlease
        GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes
        HAVE_BSD_SYSCTL = YesPlease
+       PAGER_ENV = LESS=FRX LV=-c MORE=FRX
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
index 10f6d52254335edeb2253df5b9e63bb4d5608150..9c8f7380d0f1ad76f0163209e5005adfe46ec916 100644 (file)
@@ -1008,8 +1008,8 @@ _git_branch ()
        while [ $c -lt $cword ]; do
                i="${words[c]}"
                case "$i" in
-               -d|-m)  only_local_ref="y" ;;
-               -r)     has_r="y" ;;
+               -d|--delete|-m|--move)  only_local_ref="y" ;;
+               -r|--remotes)           has_r="y" ;;
                esac
                ((c++))
        done
@@ -1023,7 +1023,7 @@ _git_branch ()
                        --color --no-color --verbose --abbrev= --no-abbrev
                        --track --no-track --contains --merged --no-merged
                        --set-upstream-to= --edit-description --list
-                       --unset-upstream
+                       --unset-upstream --delete --move --remotes
                        "
                ;;
        *)
@@ -1136,6 +1136,7 @@ _git_clone ()
                        --depth
                        --single-branch
                        --branch
+                       --recurse-submodules
                        "
                return
                ;;
@@ -1204,6 +1205,8 @@ _git_describe ()
 
 __git_diff_algorithms="myers minimal patience histogram"
 
+__git_diff_submodule_formats="log short"
+
 __git_diff_common_options="--stat --numstat --shortstat --summary
                        --patch-with-stat --name-only --name-status --color
                        --no-color --color-words --no-renames --check
@@ -1219,6 +1222,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
                        --dirstat --dirstat= --dirstat-by-file
                        --dirstat-by-file= --cumulative
                        --diff-algorithm=
+                       --submodule --submodule=
 "
 
 _git_diff ()
@@ -1230,6 +1234,10 @@ _git_diff ()
                __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
                return
                ;;
+       --submodule=*)
+               __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
+               return
+               ;;
        --*)
                __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
                        --base --ours --theirs --no-index
@@ -1493,6 +1501,14 @@ _git_log ()
                __gitcomp "full short no" "" "${cur##--decorate=}"
                return
                ;;
+       --diff-algorithm=*)
+               __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
+               return
+               ;;
+       --submodule=*)
+               __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
+               return
+               ;;
        --*)
                __gitcomp "
                        $__git_log_common_options
@@ -2181,6 +2197,7 @@ _git_config ()
                format.attach
                format.cc
                format.coverLetter
+               format.from
                format.headers
                format.numbered
                format.pretty
@@ -2455,6 +2472,10 @@ _git_show ()
                __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
                return
                ;;
+       --submodule=*)
+               __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
+               return
+               ;;
        --*)
                __gitcomp "--pretty= --format= --abbrev-commit --oneline
                        --show-signature
index 95438e1ed42f7289131a1b08654a2b23098c8e26..d60b4315ed60ad10e849408c6986d1ea5b47b32b 100755 (executable)
                $mtime = oct $mtime;
                next if $typeflag == 5; # directory
 
-               print FI "blob\n", "mark :$next_mark\n";
-               if ($typeflag == 2) { # symbolic link
-                       print FI "data ", length($linkname), "\n", $linkname;
-                       $mode = 0120000;
-               } else {
-                       print FI "data $size\n";
-                       while ($size > 0 && read(I, $_, 512) == 512) {
-                               print FI substr($_, 0, $size);
-                               $size -= 512;
+               if ($typeflag != 1) { # handle hard links later
+                       print FI "blob\n", "mark :$next_mark\n";
+                       if ($typeflag == 2) { # symbolic link
+                               print FI "data ", length($linkname), "\n",
+                                       $linkname;
+                               $mode = 0120000;
+                       } else {
+                               print FI "data $size\n";
+                               while ($size > 0 && read(I, $_, 512) == 512) {
+                                       print FI substr($_, 0, $size);
+                                       $size -= 512;
+                               }
                        }
+                       print FI "\n";
                }
-               print FI "\n";
 
                my $path;
                if ($prefix) {
                } else {
                        $path = "$name";
                }
-               $files{$path} = [$next_mark++, $mode];
+
+               if ($typeflag == 1) { # hard link
+                       $linkname = "$prefix/$linkname" if $prefix;
+                       $files{$path} = [ $files{$linkname}->[0], $mode ];
+               } else {
+                       $files{$path} = [$next_mark++, $mode];
+               }
 
                $author_time = $mtime if $mtime > $author_time;
                $path =~ m,^([^/]+)/,;
index b567eaeff97bca0b42c7217a99cd73ea4b191f07..dec085a235f4477c9079dc643c76db166f487761 100755 (executable)
@@ -4,8 +4,9 @@
 #
 # Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com>
 #
-if [ $# -eq 0 ]; then
-    set -- -h
+if test $# -eq 0
+then
+       set -- -h
 fi
 OPTS_SPEC="\
 git subtree add   --prefix=<prefix> <commit>
@@ -48,89 +49,144 @@ squash=
 message=
 prefix=
 
-debug()
-{
-       if [ -n "$debug" ]; then
+debug () {
+       if test -n "$debug"
+       then
                printf "%s\n" "$*" >&2
        fi
 }
 
-say()
-{
-       if [ -z "$quiet" ]; then
+say () {
+       if test -z "$quiet"
+       then
                printf "%s\n" "$*" >&2
        fi
 }
 
-progress()
-{
-       if [ -z "$quiet" ]; then
+progress () {
+       if test -z "$quiet"
+       then
                printf "%s\r" "$*" >&2
        fi
 }
 
-assert()
-{
-       if "$@"; then
-               :
-       else
+assert () {
+       if ! "$@"
+       then
                die "assertion failed: " "$@"
        fi
 }
 
 
-#echo "Options: $*"
-
-while [ $# -gt 0 ]; do
+while test $# -gt 0
+do
        opt="$1"
        shift
+
        case "$opt" in
-               -q) quiet=1 ;;
-               -d) debug=1 ;;
-               --annotate) annotate="$1"; shift ;;
-               --no-annotate) annotate= ;;
-               -b) branch="$1"; shift ;;
-               -P) prefix="${1%/}"; shift ;;
-               -m) message="$1"; shift ;;
-               --no-prefix) prefix= ;;
-               --onto) onto="$1"; shift ;;
-               --no-onto) onto= ;;
-               --rejoin) rejoin=1 ;;
-               --no-rejoin) rejoin= ;;
-               --ignore-joins) ignore_joins=1 ;;
-               --no-ignore-joins) ignore_joins= ;;
-               --squash) squash=1 ;;
-               --no-squash) squash= ;;
-               --) break ;;
-               *) die "Unexpected option: $opt" ;;
+       -q)
+               quiet=1
+               ;;
+       -d)
+               debug=1
+               ;;
+       --annotate)
+               annotate="$1"
+               shift
+               ;;
+       --no-annotate)
+               annotate=
+               ;;
+       -b)
+               branch="$1"
+               shift
+               ;;
+       -P)
+               prefix="${1%/}"
+               shift
+               ;;
+       -m)
+               message="$1"
+               shift
+               ;;
+       --no-prefix)
+               prefix=
+               ;;
+       --onto)
+               onto="$1"
+               shift
+               ;;
+       --no-onto)
+               onto=
+               ;;
+       --rejoin)
+               rejoin=1
+               ;;
+       --no-rejoin)
+               rejoin=
+               ;;
+       --ignore-joins)
+               ignore_joins=1
+               ;;
+       --no-ignore-joins)
+               ignore_joins=
+               ;;
+       --squash)
+               squash=1
+               ;;
+       --no-squash)
+               squash=
+               ;;
+       --)
+               break
+               ;;
+       *)
+               die "Unexpected option: $opt"
+               ;;
        esac
 done
 
 command="$1"
 shift
+
 case "$command" in
-       add|merge|pull) default= ;;
-       split|push) default="--default HEAD" ;;
-       *) die "Unknown command '$command'" ;;
+add|merge|pull)
+       default=
+       ;;
+split|push)
+       default="--default HEAD"
+       ;;
+*)
+       die "Unknown command '$command'"
+       ;;
 esac
 
-if [ -z "$prefix" ]; then
+if test -z "$prefix"
+then
        die "You must provide the --prefix option."
 fi
 
 case "$command" in
-       add) [ -e "$prefix" ] && 
-               die "prefix '$prefix' already exists." ;;
-       *)   [ -e "$prefix" ] || 
-               die "'$prefix' does not exist; use 'git subtree add'" ;;
+add)
+       test -e "$prefix" &&
+               die "prefix '$prefix' already exists."
+       ;;
+*)
+       test -e "$prefix" ||
+               die "'$prefix' does not exist; use 'git subtree add'"
+       ;;
 esac
 
 dir="$(dirname "$prefix/.")"
 
-if [ "$command" != "pull" -a "$command" != "add" -a "$command" != "push" ]; then
+if test "$command" != "pull" &&
+               test "$command" != "add" &&
+               test "$command" != "push"
+then
        revs=$(git rev-parse $default --revs-only "$@") || exit $?
-       dirs="$(git rev-parse --no-revs --no-flags "$@")" || exit $?
-       if [ -n "$dirs" ]; then
+       dirs=$(git rev-parse --no-revs --no-flags "$@") || exit $?
+       if test -n "$dirs"
+       then
                die "Error: Use --prefix instead of bare filenames."
        fi
 fi
@@ -142,78 +198,82 @@ debug "dir: {$dir}"
 debug "opts: {$*}"
 debug
 
-cache_setup()
-{
+cache_setup () {
        cachedir="$GIT_DIR/subtree-cache/$$"
-       rm -rf "$cachedir" || die "Can't delete old cachedir: $cachedir"
-       mkdir -p "$cachedir" || die "Can't create new cachedir: $cachedir"
-       mkdir -p "$cachedir/notree" || die "Can't create new cachedir: $cachedir/notree"
+       rm -rf "$cachedir" ||
+               die "Can't delete old cachedir: $cachedir"
+       mkdir -p "$cachedir" ||
+               die "Can't create new cachedir: $cachedir"
+       mkdir -p "$cachedir/notree" ||
+               die "Can't create new cachedir: $cachedir/notree"
        debug "Using cachedir: $cachedir" >&2
 }
 
-cache_get()
-{
-       for oldrev in $*; do
-               if [ -r "$cachedir/$oldrev" ]; then
+cache_get () {
+       for oldrev in "$@"
+       do
+               if test -r "$cachedir/$oldrev"
+               then
                        read newrev <"$cachedir/$oldrev"
                        echo $newrev
                fi
        done
 }
 
-cache_miss()
-{
-       for oldrev in $*; do
-               if [ ! -r "$cachedir/$oldrev" ]; then
+cache_miss () {
+       for oldrev in "$@"
+       do
+               if ! test -r "$cachedir/$oldrev"
+               then
                        echo $oldrev
                fi
        done
 }
 
-check_parents()
-{
-       missed=$(cache_miss $*)
-       for miss in $missed; do
-               if [ ! -r "$cachedir/notree/$miss" ]; then
+check_parents () {
+       missed=$(cache_miss "$@")
+       for miss in $missed
+       do
+               if ! test -r "$cachedir/notree/$miss"
+               then
                        debug "  incorrect order: $miss"
                fi
        done
 }
 
-set_notree()
-{
+set_notree () {
        echo "1" > "$cachedir/notree/$1"
 }
 
-cache_set()
-{
+cache_set () {
        oldrev="$1"
        newrev="$2"
-       if [ "$oldrev" != "latest_old" \
-            -a "$oldrev" != "latest_new" \
-            -a -e "$cachedir/$oldrev" ]; then
+       if test "$oldrev" != "latest_old" &&
+               test "$oldrev" != "latest_new" &&
+               test -e "$cachedir/$oldrev"
+       then
                die "cache for $oldrev already exists!"
        fi
        echo "$newrev" >"$cachedir/$oldrev"
 }
 
-rev_exists()
-{
-       if git rev-parse "$1" >/dev/null 2>&1; then
+rev_exists () {
+       if git rev-parse "$1" >/dev/null 2>&1
+       then
                return 0
        else
                return 1
        fi
 }
 
-rev_is_descendant_of_branch()
-{
+rev_is_descendant_of_branch () {
        newrev="$1"
        branch="$2"
-       branch_hash=$(git rev-parse $branch)
-       match=$(git rev-list -1 $branch_hash ^$newrev)
+       branch_hash=$(git rev-parse "$branch")
+       match=$(git rev-list -1 "$branch_hash" "^$newrev")
 
-       if [ -z "$match" ]; then
+       if test -z "$match"
+       then
                return 0
        else
                return 1
@@ -223,15 +283,14 @@ rev_is_descendant_of_branch()
 # if a commit doesn't have a parent, this might not work.  But we only want
 # to remove the parent from the rev-list, and since it doesn't exist, it won't
 # be there anyway, so do nothing in that case.
-try_remove_previous()
-{
-       if rev_exists "$1^"; then
+try_remove_previous () {
+       if rev_exists "$1^"
+       then
                echo "^$1^"
        fi
 }
 
-find_latest_squash()
-{
+find_latest_squash () {
        debug "Looking for latest squash ($dir)..."
        dir="$1"
        sq=
@@ -239,37 +298,43 @@ find_latest_squash()
        sub=
        git log --grep="^git-subtree-dir: $dir/*\$" \
                --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD |
-       while read a b junk; do
+       while read a b junk
+       do
                debug "$a $b $junk"
                debug "{{$sq/$main/$sub}}"
                case "$a" in
-                       START) sq="$b" ;;
-                       git-subtree-mainline:) main="$b" ;;
-                       git-subtree-split:)
-                               sub="$(git rev-parse "$b^0")" ||
-                                   die "could not rev-parse split hash $b from commit $sq"
-                               ;;
-                       END)
-                               if [ -n "$sub" ]; then
-                                       if [ -n "$main" ]; then
-                                               # a rejoin commit?
-                                               # Pretend its sub was a squash.
-                                               sq="$sub"
-                                       fi
-                                       debug "Squash found: $sq $sub"
-                                       echo "$sq" "$sub"
-                                       break
+               START)
+                       sq="$b"
+                       ;;
+               git-subtree-mainline:)
+                       main="$b"
+                       ;;
+               git-subtree-split:)
+                       sub="$(git rev-parse "$b^0")" ||
+                       die "could not rev-parse split hash $b from commit $sq"
+                       ;;
+               END)
+                       if test -n "$sub"
+                       then
+                               if test -n "$main"
+                               then
+                                       # a rejoin commit?
+                                       # Pretend its sub was a squash.
+                                       sq="$sub"
                                fi
-                               sq=
-                               main=
-                               sub=
-                               ;;
+                               debug "Squash found: $sq $sub"
+                               echo "$sq" "$sub"
+                               break
+                       fi
+                       sq=
+                       main=
+                       sub=
+                       ;;
                esac
        done
 }
 
-find_existing_splits()
-{
+find_existing_splits () {
        debug "Looking for prior splits..."
        dir="$1"
        revs="$2"
@@ -277,37 +342,43 @@ find_existing_splits()
        sub=
        git log --grep="^git-subtree-dir: $dir/*\$" \
                --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
-       while read a b junk; do
+       while read a b junk
+       do
                case "$a" in
-                       START) sq="$b" ;;
-                       git-subtree-mainline:) main="$b" ;;
-                       git-subtree-split:)
-                               sub="$(git rev-parse "$b^0")" ||
-                                   die "could not rev-parse split hash $b from commit $sq"
-                               ;;
-                       END)
-                               debug "  Main is: '$main'"
-                               if [ -z "$main" -a -n "$sub" ]; then
-                                       # squash commits refer to a subtree
-                                       debug "  Squash: $sq from $sub"
-                                       cache_set "$sq" "$sub"
-                               fi
-                               if [ -n "$main" -a -n "$sub" ]; then
-                                       debug "  Prior: $main -> $sub"
-                                       cache_set $main $sub
-                                       cache_set $sub $sub
-                                       try_remove_previous "$main"
-                                       try_remove_previous "$sub"
-                               fi
-                               main=
-                               sub=
-                               ;;
+               START)
+                       sq="$b"
+                       ;;
+               git-subtree-mainline:)
+                       main="$b"
+                       ;;
+               git-subtree-split:)
+                       sub="$(git rev-parse "$b^0")" ||
+                       die "could not rev-parse split hash $b from commit $sq"
+                       ;;
+               END)
+                       debug "  Main is: '$main'"
+                       if test -z "$main" -a -n "$sub"
+                       then
+                               # squash commits refer to a subtree
+                               debug "  Squash: $sq from $sub"
+                               cache_set "$sq" "$sub"
+                       fi
+                       if test -n "$main" -a -n "$sub"
+                       then
+                               debug "  Prior: $main -> $sub"
+                               cache_set $main $sub
+                               cache_set $sub $sub
+                               try_remove_previous "$main"
+                               try_remove_previous "$sub"
+                       fi
+                       main=
+                       sub=
+                       ;;
                esac
        done
 }
 
-copy_commit()
-{
+copy_commit () {
        # We're going to set some environment vars here, so
        # do it in a subshell to get rid of them safely later
        debug copy_commit "{$1}" "{$2}" "{$3}"
@@ -325,66 +396,69 @@ copy_commit()
                        GIT_COMMITTER_NAME \
                        GIT_COMMITTER_EMAIL \
                        GIT_COMMITTER_DATE
-               (printf "%s" "$annotate"; cat ) |
+               (
+                       printf "%s" "$annotate"
+                       cat
+               ) |
                git commit-tree "$2" $3  # reads the rest of stdin
        ) || die "Can't copy commit $1"
 }
 
-add_msg()
-{
+add_msg () {
        dir="$1"
        latest_old="$2"
        latest_new="$3"
-       if [ -n "$message" ]; then
+       if test -n "$message"
+       then
                commit_message="$message"
        else
                commit_message="Add '$dir/' from commit '$latest_new'"
        fi
        cat <<-EOF
                $commit_message
-               
+
                git-subtree-dir: $dir
                git-subtree-mainline: $latest_old
                git-subtree-split: $latest_new
        EOF
 }
 
-add_squashed_msg()
-{
-       if [ -n "$message" ]; then
+add_squashed_msg () {
+       if test -n "$message"
+       then
                echo "$message"
        else
                echo "Merge commit '$1' as '$2'"
        fi
 }
 
-rejoin_msg()
-{
+rejoin_msg () {
        dir="$1"
        latest_old="$2"
        latest_new="$3"
-       if [ -n "$message" ]; then
+       if test -n "$message"
+       then
                commit_message="$message"
        else
                commit_message="Split '$dir/' into commit '$latest_new'"
        fi
        cat <<-EOF
                $commit_message
-               
+
                git-subtree-dir: $dir
                git-subtree-mainline: $latest_old
                git-subtree-split: $latest_new
        EOF
 }
 
-squash_msg()
-{
+squash_msg () {
        dir="$1"
        oldsub="$2"
        newsub="$3"
        newsub_short=$(git rev-parse --short "$newsub")
-       
-       if [ -n "$oldsub" ]; then
+
+       if test -n "$oldsub"
+       then
                oldsub_short=$(git rev-parse --short "$oldsub")
                echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short"
                echo
@@ -393,41 +467,41 @@ squash_msg()
        else
                echo "Squashed '$dir/' content from commit $newsub_short"
        fi
-       
+
        echo
        echo "git-subtree-dir: $dir"
        echo "git-subtree-split: $newsub"
 }
 
-toptree_for_commit()
-{
+toptree_for_commit () {
        commit="$1"
        git log -1 --pretty=format:'%T' "$commit" -- || exit $?
 }
 
-subtree_for_commit()
-{
+subtree_for_commit () {
        commit="$1"
        dir="$2"
        git ls-tree "$commit" -- "$dir" |
-       while read mode type tree name; do
-               assert [ "$name" = "$dir" ]
-               assert [ "$type" = "tree" -o "$type" = "commit" ]
-               [ "$type" = "commit" ] && continue  # ignore submodules
+       while read mode type tree name
+       do
+               assert test "$name" = "$dir"
+               assert test "$type" = "tree" -o "$type" = "commit"
+               test "$type" = "commit" && continue  # ignore submodules
                echo $tree
                break
        done
 }
 
-tree_changed()
-{
+tree_changed () {
        tree=$1
        shift
-       if [ $# -ne 1 ]; then
+       if test $# -ne 1
+       then
                return 0   # weird parents, consider it changed
        else
                ptree=$(toptree_for_commit $1)
-               if [ "$ptree" != "$tree" ]; then
+               if test "$ptree" != "$tree"
+               then
                        return 0   # changed
                else
                        return 1   # not changed
@@ -435,118 +509,127 @@ tree_changed()
        fi
 }
 
-new_squash_commit()
-{
+new_squash_commit () {
        old="$1"
        oldsub="$2"
        newsub="$3"
        tree=$(toptree_for_commit $newsub) || exit $?
-       if [ -n "$old" ]; then
-               squash_msg "$dir" "$oldsub" "$newsub" | 
-                       git commit-tree "$tree" -p "$old" || exit $?
+       if test -n "$old"
+       then
+               squash_msg "$dir" "$oldsub" "$newsub" |
+               git commit-tree "$tree" -p "$old" || exit $?
        else
                squash_msg "$dir" "" "$newsub" |
-                       git commit-tree "$tree" || exit $?
+               git commit-tree "$tree" || exit $?
        fi
 }
 
-copy_or_skip()
-{
+copy_or_skip () {
        rev="$1"
        tree="$2"
        newparents="$3"
-       assert [ -n "$tree" ]
+       assert test -n "$tree"
 
        identical=
        nonidentical=
        p=
        gotparents=
-       for parent in $newparents; do
+       for parent in $newparents
+       do
                ptree=$(toptree_for_commit $parent) || exit $?
-               [ -z "$ptree" ] && continue
-               if [ "$ptree" = "$tree" ]; then
+               test -z "$ptree" && continue
+               if test "$ptree" = "$tree"
+               then
                        # an identical parent could be used in place of this rev.
                        identical="$parent"
                else
                        nonidentical="$parent"
                fi
-               
+
                # sometimes both old parents map to the same newparent;
                # eliminate duplicates
                is_new=1
-               for gp in $gotparents; do
-                       if [ "$gp" = "$parent" ]; then
+               for gp in $gotparents
+               do
+                       if test "$gp" = "$parent"
+                       then
                                is_new=
                                break
                        fi
                done
-               if [ -n "$is_new" ]; then
+               if test -n "$is_new"
+               then
                        gotparents="$gotparents $parent"
                        p="$p -p $parent"
                fi
        done
 
        copycommit=
-       if [ -n "$identical" ] && [ -n "$nonidentical" ]; then
+       if test -n "$identical" && test -n "$nonidentical"
+       then
                extras=$(git rev-list --count $identical..$nonidentical)
-               if [ "$extras" -ne 0 ]; then
+               if test "$extras" -ne 0
+               then
                        # we need to preserve history along the other branch
                        copycommit=1
                fi
        fi
-       if [ -n "$identical" ] && [ -z "$copycommit" ]; then
+       if test -n "$identical" && test -z "$copycommit"
+       then
                echo $identical
        else
-               copy_commit $rev $tree "$p" || exit $?
+               copy_commit "$rev" "$tree" "$p" || exit $?
        fi
 }
 
-ensure_clean()
-{
-       if ! git diff-index HEAD --exit-code --quiet 2>&1; then
+ensure_clean () {
+       if ! git diff-index HEAD --exit-code --quiet 2>&1
+       then
                die "Working tree has modifications.  Cannot add."
        fi
-       if ! git diff-index --cached HEAD --exit-code --quiet 2>&1; then
+       if ! git diff-index --cached HEAD --exit-code --quiet 2>&1
+       then
                die "Index has modifications.  Cannot add."
        fi
 }
 
-ensure_valid_ref_format()
-{
+ensure_valid_ref_format () {
        git check-ref-format "refs/heads/$1" ||
-           die "'$1' does not look like a ref"
+               die "'$1' does not look like a ref"
 }
 
-cmd_add()
-{
-       if [ -e "$dir" ]; then
+cmd_add () {
+       if test -e "$dir"
+       then
                die "'$dir' already exists.  Cannot add."
        fi
 
        ensure_clean
-       
-       if [ $# -eq 1 ]; then
-           git rev-parse -q --verify "$1^{commit}" >/dev/null ||
-           die "'$1' does not refer to a commit"
-
-           "cmd_add_commit" "$@"
-       elif [ $# -eq 2 ]; then
-           # Technically we could accept a refspec here but we're
-           # just going to turn around and add FETCH_HEAD under the
-           # specified directory.  Allowing a refspec might be
-           # misleading because we won't do anything with any other
-           # branches fetched via the refspec.
-           ensure_valid_ref_format "$2"
-
-           "cmd_add_repository" "$@"
+
+       if test $# -eq 1
+       then
+               git rev-parse -q --verify "$1^{commit}" >/dev/null ||
+                       die "'$1' does not refer to a commit"
+
+               cmd_add_commit "$@"
+
+       elif test $# -eq 2
+       then
+               # Technically we could accept a refspec here but we're
+               # just going to turn around and add FETCH_HEAD under the
+               # specified directory.  Allowing a refspec might be
+               # misleading because we won't do anything with any other
+               # branches fetched via the refspec.
+               ensure_valid_ref_format "$2"
+
+               cmd_add_repository "$@"
        else
-           say "error: parameters were '$@'"
-           die "Provide either a commit or a repository and commit."
+               say "error: parameters were '$@'"
+               die "Provide either a commit or a repository and commit."
        fi
 }
 
-cmd_add_repository()
-{
+cmd_add_repository () {
        echo "git fetch" "$@"
        repository=$1
        refspec=$2
@@ -556,60 +639,63 @@ cmd_add_repository()
        cmd_add_commit "$@"
 }
 
-cmd_add_commit()
-{
+cmd_add_commit () {
        revs=$(git rev-parse $default --revs-only "$@") || exit $?
        set -- $revs
        rev="$1"
-       
+
        debug "Adding $dir as '$rev'..."
        git read-tree --prefix="$dir" $rev || exit $?
        git checkout -- "$dir" || exit $?
        tree=$(git write-tree) || exit $?
-       
+
        headrev=$(git rev-parse HEAD) || exit $?
-       if [ -n "$headrev" -a "$headrev" != "$rev" ]; then
+       if test -n "$headrev" && test "$headrev" != "$rev"
+       then
                headp="-p $headrev"
        else
                headp=
        fi
-       
-       if [ -n "$squash" ]; then
+
+       if test -n "$squash"
+       then
                rev=$(new_squash_commit "" "" "$rev") || exit $?
                commit=$(add_squashed_msg "$rev" "$dir" |
-                        git commit-tree $tree $headp -p "$rev") || exit $?
+                       git commit-tree "$tree" $headp -p "$rev") || exit $?
        else
                revp=$(peel_committish "$rev") &&
-               commit=$(add_msg "$dir" "$headrev" "$rev" |
-                        git commit-tree $tree $headp -p "$revp") || exit $?
+               commit=$(add_msg "$dir" $headrev "$rev" |
+                       git commit-tree "$tree" $headp -p "$revp") || exit $?
        fi
        git reset "$commit" || exit $?
-       
+
        say "Added dir '$dir'"
 }
 
-cmd_split()
-{
+cmd_split () {
        debug "Splitting $dir..."
        cache_setup || exit $?
-       
-       if [ -n "$onto" ]; then
+
+       if test -n "$onto"
+       then
                debug "Reading history for --onto=$onto..."
                git rev-list $onto |
-               while read rev; do
+               while read rev
+               do
                        # the 'onto' history is already just the subdir, so
                        # any parent we find there can be used verbatim
                        debug "  cache: $rev"
-                       cache_set $rev $rev
+                       cache_set "$rev" "$rev"
                done
        fi
-       
-       if [ -n "$ignore_joins" ]; then
+
+       if test -n "$ignore_joins"
+       then
                unrevs=
        else
                unrevs="$(find_existing_splits "$dir" "$revs")"
        fi
-       
+
        # We can't restrict rev-list to only $dir here, because some of our
        # parents have the $dir contents the root, and those won't match.
        # (and rev-list --follow doesn't seem to solve this)
@@ -618,12 +704,14 @@ cmd_split()
        revcount=0
        createcount=0
        eval "$grl" |
-       while read rev parents; do
+       while read rev parents
+       do
                revcount=$(($revcount + 1))
                progress "$revcount/$revmax ($createcount)"
                debug "Processing commit: $rev"
-               exists=$(cache_get $rev)
-               if [ -n "$exists" ]; then
+               exists=$(cache_get "$rev")
+               if test -n "$exists"
+               then
                        debug "  prior: $exists"
                        continue
                fi
@@ -631,77 +719,89 @@ cmd_split()
                debug "  parents: $parents"
                newparents=$(cache_get $parents)
                debug "  newparents: $newparents"
-               
-               tree=$(subtree_for_commit $rev "$dir")
+
+               tree=$(subtree_for_commit "$rev" "$dir")
                debug "  tree is: $tree"
 
                check_parents $parents
-               
+
                # ugly.  is there no better way to tell if this is a subtree
                # vs. a mainline commit?  Does it matter?
-               if [ -z $tree ]; then
-                       set_notree $rev
-                       if [ -n "$newparents" ]; then
-                               cache_set $rev $rev
+               if test -z "$tree"
+               then
+                       set_notree "$rev"
+                       if test -n "$newparents"
+                       then
+                               cache_set "$rev" "$rev"
                        fi
                        continue
                fi
 
                newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
                debug "  newrev is: $newrev"
-               cache_set $rev $newrev
-               cache_set latest_new $newrev
-               cache_set latest_old $rev
+               cache_set "$rev" "$newrev"
+               cache_set latest_new "$newrev"
+               cache_set latest_old "$rev"
        done || exit $?
+
        latest_new=$(cache_get latest_new)
-       if [ -z "$latest_new" ]; then
+       if test -z "$latest_new"
+       then
                die "No new revisions were found"
        fi
-       
-       if [ -n "$rejoin" ]; then
+
+       if test -n "$rejoin"
+       then
                debug "Merging split branch into HEAD..."
                latest_old=$(cache_get latest_old)
                git merge -s ours \
                        --allow-unrelated-histories \
-                       -m "$(rejoin_msg "$dir" $latest_old $latest_new)" \
-                       $latest_new >&2 || exit $?
-       fi
-       if [ -n "$branch" ]; then
-               if rev_exists "refs/heads/$branch"; then
-                       if ! rev_is_descendant_of_branch $latest_new $branch; then
+                       -m "$(rejoin_msg "$dir" "$latest_old" "$latest_new")" \
+                       "$latest_new" >&2 || exit $?
+       fi
+       if test -n "$branch"
+       then
+               if rev_exists "refs/heads/$branch"
+               then
+                       if ! rev_is_descendant_of_branch "$latest_new" "$branch"
+                       then
                                die "Branch '$branch' is not an ancestor of commit '$latest_new'."
                        fi
                        action='Updated'
                else
                        action='Created'
                fi
-               git update-ref -m 'subtree split' "refs/heads/$branch" $latest_new || exit $?
+               git update-ref -m 'subtree split' \
+                       "refs/heads/$branch" "$latest_new" || exit $?
                say "$action branch '$branch'"
        fi
-       echo $latest_new
+       echo "$latest_new"
        exit 0
 }
 
-cmd_merge()
-{
+cmd_merge () {
        revs=$(git rev-parse $default --revs-only "$@") || exit $?
        ensure_clean
-       
+
        set -- $revs
-       if [ $# -ne 1 ]; then
+       if test $# -ne 1
+       then
                die "You must provide exactly one revision.  Got: '$revs'"
        fi
        rev="$1"
-       
-       if [ -n "$squash" ]; then
+
+       if test -n "$squash"
+       then
                first_split="$(find_latest_squash "$dir")"
-               if [ -z "$first_split" ]; then
+               if test -z "$first_split"
+               then
                        die "Can't squash-merge: '$dir' was never added."
                fi
                set $first_split
                old=$1
                sub=$2
-               if [ "$sub" = "$rev" ]; then
+               if test "$sub" = "$rev"
+               then
                        say "Subtree is already at commit $rev."
                        exit 0
                fi
@@ -711,25 +811,29 @@ cmd_merge()
        fi
 
        version=$(git version)
-       if [ "$version" \< "git version 1.7" ]; then
-               if [ -n "$message" ]; then
-                       git merge -s subtree --message="$message" $rev
+       if test "$version" \< "git version 1.7"
+       then
+               if test -n "$message"
+               then
+                       git merge -s subtree --message="$message" "$rev"
                else
-                       git merge -s subtree $rev
+                       git merge -s subtree "$rev"
                fi
        else
-               if [ -n "$message" ]; then
-                       git merge -Xsubtree="$prefix" --message="$message" $rev
+               if test -n "$message"
+               then
+                       git merge -Xsubtree="$prefix" \
+                               --message="$message" "$rev"
                else
                        git merge -Xsubtree="$prefix" $rev
                fi
        fi
 }
 
-cmd_pull()
-{
-       if [ $# -ne 2 ]; then
-           die "You must provide <repository> <ref>"
+cmd_pull () {
+       if test $# -ne 2
+       then
+               die "You must provide <repository> <ref>"
        fi
        ensure_clean
        ensure_valid_ref_format "$2"
@@ -739,20 +843,21 @@ cmd_pull()
        cmd_merge "$@"
 }
 
-cmd_push()
-{
-       if [ $# -ne 2 ]; then
-           die "You must provide <repository> <ref>"
+cmd_push () {
+       if test $# -ne 2
+       then
+               die "You must provide <repository> <ref>"
        fi
        ensure_valid_ref_format "$2"
-       if [ -e "$dir" ]; then
-           repository=$1
-           refspec=$2
-           echo "git push using: " $repository $refspec
-           localrev=$(git subtree split --prefix="$prefix") || die
-           git push "$repository" $localrev:refs/heads/$refspec
+       if test -e "$dir"
+       then
+               repository=$1
+               refspec=$2
+               echo "git push using: " "$repository" "$refspec"
+               localrev=$(git subtree split --prefix="$prefix") || die
+               git push "$repository" "$localrev":"refs/heads/$refspec"
        else
-           die "'$dir' must already exist. Try 'git subtree add'."
+               die "'$dir' must already exist. Try 'git subtree add'."
        fi
 }
 
diff --git a/date.c b/date.c
index 4c7aa9ba853c0924d49f4b8c2cc06826fe08956c..a996331f5b33703c9f70844c6b49453ec39d16a7 100644 (file)
--- a/date.c
+++ b/date.c
@@ -177,6 +177,12 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
        struct tm *tm;
        static struct strbuf timebuf = STRBUF_INIT;
 
+       if (mode->type == DATE_UNIX) {
+               strbuf_reset(&timebuf);
+               strbuf_addf(&timebuf, "%lu", time);
+               return timebuf.buf;
+       }
+
        if (mode->local)
                tz = local_tzoffset(time);
 
@@ -792,6 +798,8 @@ static enum date_mode_type parse_date_type(const char *format, const char **end)
                return DATE_NORMAL;
        if (skip_prefix(format, "raw", end))
                return DATE_RAW;
+       if (skip_prefix(format, "unix", end))
+               return DATE_UNIX;
        if (skip_prefix(format, "format", end))
                return DATE_STRFTIME;
 
index 58ac0a531be0722bc422eb008bfcb9695965996c..73d003a08ae72d0c8f18b16172bc09c86022eb8f 100644 (file)
@@ -541,7 +541,7 @@ void diffcore_rename(struct diff_options *options)
                                rename_dst_nr * rename_src_nr, 50, 1);
        }
 
-       mx = xcalloc(st_mult(num_create, NUM_CANDIDATE_PER_DST), sizeof(*mx));
+       mx = xcalloc(st_mult(NUM_CANDIDATE_PER_DST, num_create), sizeof(*mx));
        for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
                struct diff_filespec *two = rename_dst[i].two;
                struct diff_score *m;
index ebd13baa6e06cf7007fdd5c48713f06cf511721d..a5790d03a075884ffe93dbff782b28ec87263051 100755 (executable)
@@ -37,14 +37,6 @@ sub usage
        exit($exitcode);
 }
 
-sub find_worktree
-{
-       # Git->repository->wc_path() does not honor changes to the working
-       # tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree'
-       # config variable.
-       return Git::command_oneline('rev-parse', '--show-toplevel');
-}
-
 sub print_tool_help
 {
        # See the comment at the bottom of file_diff() for the reason behind
@@ -67,14 +59,14 @@ sub exit_cleanup
 
 sub use_wt_file
 {
-       my ($repo, $workdir, $file, $sha1) = @_;
+       my ($workdir, $file, $sha1) = @_;
        my $null_sha1 = '0' x 40;
 
        if (-l "$workdir/$file" || ! -e _) {
                return (0, $null_sha1);
        }
 
-       my $wt_sha1 = $repo->command_oneline('hash-object', "$workdir/$file");
+       my $wt_sha1 = Git::command_oneline('hash-object', "$workdir/$file");
        my $use = ($sha1 eq $null_sha1) || ($sha1 eq $wt_sha1);
        return ($use, $wt_sha1);
 }
@@ -83,20 +75,17 @@ sub changed_files
 {
        my ($repo_path, $index, $worktree) = @_;
        $ENV{GIT_INDEX_FILE} = $index;
-       $ENV{GIT_WORK_TREE} = $worktree;
-       my $must_unset_git_dir = 0;
-       if (not defined($ENV{GIT_DIR})) {
-               $must_unset_git_dir = 1;
-               $ENV{GIT_DIR} = $repo_path;
-       }
 
-       my @refreshargs = qw/update-index --really-refresh -q --unmerged/;
-       my @gitargs = qw/diff-files --name-only -z/;
+       my @gitargs = ('--git-dir', $repo_path, '--work-tree', $worktree);
+       my @refreshargs = (
+               @gitargs, 'update-index',
+               '--really-refresh', '-q', '--unmerged');
        try {
                Git::command_oneline(@refreshargs);
        } catch Git::Error::Command with {};
 
-       my $line = Git::command_oneline(@gitargs);
+       my @diffargs = (@gitargs, 'diff-files', '--name-only', '-z');
+       my $line = Git::command_oneline(@diffargs);
        my @files;
        if (defined $line) {
                @files = split('\0', $line);
@@ -105,26 +94,15 @@ sub changed_files
        }
 
        delete($ENV{GIT_INDEX_FILE});
-       delete($ENV{GIT_WORK_TREE});
-       delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
 
        return map { $_ => 1 } @files;
 }
 
 sub setup_dir_diff
 {
-       my ($repo, $workdir, $symlinks) = @_;
-
-       # Run the diff; exit immediately if no diff found
-       # 'Repository' and 'WorkingCopy' must be explicitly set to insure that
-       # if $GIT_DIR and $GIT_WORK_TREE are set in ENV, they are actually used
-       # by Git->repository->command*.
-       my $repo_path = $repo->repo_path();
-       my %repo_args = (Repository => $repo_path, WorkingCopy => $workdir);
-       my $diffrepo = Git->repository(%repo_args);
-
+       my ($workdir, $symlinks) = @_;
        my @gitargs = ('diff', '--raw', '--no-abbrev', '-z', @ARGV);
-       my $diffrtn = $diffrepo->command_oneline(@gitargs);
+       my $diffrtn = Git::command_oneline(@gitargs);
        exit(0) unless defined($diffrtn);
 
        # Build index info for left and right sides of the diff
@@ -176,12 +154,12 @@ sub setup_dir_diff
 
                if ($lmode eq $symlink_mode) {
                        $symlink{$src_path}{left} =
-                               $diffrepo->command_oneline('show', "$lsha1");
+                               Git::command_oneline('show', $lsha1);
                }
 
                if ($rmode eq $symlink_mode) {
                        $symlink{$dst_path}{right} =
-                               $diffrepo->command_oneline('show', "$rsha1");
+                               Git::command_oneline('show', $rsha1);
                }
 
                if ($lmode ne $null_mode and $status !~ /^C/) {
@@ -193,8 +171,8 @@ sub setup_dir_diff
                        if ($working_tree_dups{$dst_path}++) {
                                next;
                        }
-                       my ($use, $wt_sha1) = use_wt_file($repo, $workdir,
-                                                         $dst_path, $rsha1);
+                       my ($use, $wt_sha1) =
+                               use_wt_file($workdir, $dst_path, $rsha1);
                        if ($use) {
                                push @working_tree, $dst_path;
                                $wtindex .= "$rmode $wt_sha1\t$dst_path\0";
@@ -211,44 +189,34 @@ sub setup_dir_diff
        mkpath($ldir) or exit_cleanup($tmpdir, 1);
        mkpath($rdir) or exit_cleanup($tmpdir, 1);
 
-       # If $GIT_DIR is not set prior to calling 'git update-index' and
-       # 'git checkout-index', then those commands will fail if difftool
-       # is called from a directory other than the repo root.
-       my $must_unset_git_dir = 0;
-       if (not defined($ENV{GIT_DIR})) {
-               $must_unset_git_dir = 1;
-               $ENV{GIT_DIR} = $repo_path;
-       }
-
        # Populate the left and right directories based on each index file
        my ($inpipe, $ctx);
        $ENV{GIT_INDEX_FILE} = "$tmpdir/lindex";
        ($inpipe, $ctx) =
-               $repo->command_input_pipe(qw(update-index -z --index-info));
+               Git::command_input_pipe('update-index', '-z', '--index-info');
        print($inpipe $lindex);
-       $repo->command_close_pipe($inpipe, $ctx);
+       Git::command_close_pipe($inpipe, $ctx);
 
        my $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/");
        exit_cleanup($tmpdir, $rc) if $rc != 0;
 
        $ENV{GIT_INDEX_FILE} = "$tmpdir/rindex";
        ($inpipe, $ctx) =
-               $repo->command_input_pipe(qw(update-index -z --index-info));
+               Git::command_input_pipe('update-index', '-z', '--index-info');
        print($inpipe $rindex);
-       $repo->command_close_pipe($inpipe, $ctx);
+       Git::command_close_pipe($inpipe, $ctx);
 
        $rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/");
        exit_cleanup($tmpdir, $rc) if $rc != 0;
 
        $ENV{GIT_INDEX_FILE} = "$tmpdir/wtindex";
        ($inpipe, $ctx) =
-               $repo->command_input_pipe(qw(update-index --info-only -z --index-info));
+               Git::command_input_pipe('update-index', '--info-only', '-z', '--index-info');
        print($inpipe $wtindex);
-       $repo->command_close_pipe($inpipe, $ctx);
+       Git::command_close_pipe($inpipe, $ctx);
 
        # If $GIT_DIR was explicitly set just for the update/checkout
        # commands, then it should be unset before continuing.
-       delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
        delete($ENV{GIT_INDEX_FILE});
 
        # Changes in the working tree need special treatment since they are
@@ -415,9 +383,9 @@ sub dir_diff
        my $rc;
        my $error = 0;
        my $repo = Git->repository();
-       my $workdir = find_worktree();
-       my ($a, $b, $tmpdir, @worktree) =
-               setup_dir_diff($repo, $workdir, $symlinks);
+       my $repo_path = $repo->repo_path();
+       my $workdir = $repo->wc_path();
+       my ($a, $b, $tmpdir, @worktree) = setup_dir_diff($workdir, $symlinks);
 
        if (defined($extcmd)) {
                $rc = system($extcmd, $a, $b);
@@ -443,10 +411,10 @@ sub dir_diff
                next if ! -f "$b/$file";
 
                if (!$indices_loaded) {
-                       %wt_modified = changed_files($repo->repo_path(),
-                               "$tmpdir/wtindex", "$workdir");
-                       %tmp_modified = changed_files($repo->repo_path(),
-                               "$tmpdir/wtindex", "$b");
+                       %wt_modified = changed_files(
+                               $repo_path, "$tmpdir/wtindex", $workdir);
+                       %tmp_modified = changed_files(
+                               $repo_path, "$tmpdir/wtindex", $b);
                        $indices_loaded = 1;
                }
 
index ded459563816ebe36f4e467f596f0c84fa30ff57..e2da524f5a26206b3558f5a1d062695939c054e7 100644 (file)
@@ -121,7 +121,7 @@ mark_action_done () {
        sed -e 1q < "$todo" >> "$done"
        sed -e 1d < "$todo" >> "$todo".new
        mv -f "$todo".new "$todo"
-       new_count=$(git stripspace --strip-comments <"$done" | wc -l)
+       new_count=$(( $(git stripspace --strip-comments <"$done" | wc -l) ))
        echo $new_count >"$msgnum"
        total=$(($new_count + $(git stripspace --strip-comments <"$todo" | wc -l)))
        echo $total >"$end"
index 0c34aa62f6b1b33be67f4221290c9c5ce55beabe..a8a4576342e519932be07fca5cbe2ee2fc0ab73b 100644 (file)
@@ -163,9 +163,11 @@ git_pager() {
        else
                GIT_PAGER=cat
        fi
-       : "${LESS=-FRX}"
-       : "${LV=-c}"
-       export LESS LV
+       for vardef in @@PAGER_ENV@@
+       do
+               var=${vardef%%=*}
+               eval ": \"\${$vardef}\" && export $var"
+       done
 
        eval "$GIT_PAGER" '"$@"'
 }
index c90dc335d145fa0a16efbe41873a525b596b806d..b57f87de658b627c48ec672faaab3dc6aac3b505 100755 (executable)
@@ -479,7 +479,8 @@ fetch_in_submodule () (
        '')
                git fetch ;;
        *)
-               git fetch $(get_default_remote) "$2" ;;
+               shift
+               git fetch $(get_default_remote) "$@" ;;
        esac
 )
 
@@ -588,7 +589,6 @@ cmd_update()
 
                name=$(git submodule--helper name "$sm_path") || exit
                url=$(git config submodule."$name".url)
-               branch=$(get_submodule_config "$name" branch master)
                if ! test -z "$update"
                then
                        update_module=$update
@@ -614,10 +614,11 @@ cmd_update()
 
                if test -n "$remote"
                then
+                       branch=$(git submodule--helper remote-branch "$sm_path")
                        if test -z "$nofetch"
                        then
                                # Fetch remote before determining tracking $sha1
-                               fetch_in_submodule "$sm_path" ||
+                               fetch_in_submodule "$sm_path" $depth ||
                                die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
                        fi
                        remote_name=$(sanitize_submodule_env; cd "$sm_path" && get_default_remote)
@@ -640,13 +641,13 @@ cmd_update()
                                # Run fetch only if $sha1 isn't present or it
                                # is not reachable from a ref.
                                is_tip_reachable "$sm_path" "$sha1" ||
-                               fetch_in_submodule "$sm_path" ||
+                               fetch_in_submodule "$sm_path" $depth ||
                                die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
 
                                # Now we tried the usual fetch, but $sha1 may
                                # not be reachable from any of the refs
                                is_tip_reachable "$sm_path" "$sha1" ||
-                               fetch_in_submodule "$sm_path" "$sha1" ||
+                               fetch_in_submodule "$sm_path" $depth "$sha1" ||
                                die "$(eval_gettext "Fetched in submodule path '\$displaypath', but it did not contain \$sha1. Direct fetching of that commit failed.")"
                        fi
 
index 2fddf750fabf9ac2d079777ad7bd7953c2477f9c..33d701d8525fd9334e4a899a807c8f8f0164dcc5 100755 (executable)
@@ -2090,7 +2090,7 @@ sub format_ref_marker {
                                -href => href(
                                        action=>$dest_action,
                                        hash=>$dest
-                               )}, $name);
+                               )}, esc_html($name));
 
                        $markers .= " <span class=\"".esc_attr($class)."\" title=\"".esc_attr($ref)."\">" .
                                $link . "</span>";
diff --git a/grep.c b/grep.c
index 58d599e6475cd1472c824743ec60b00361a396c3..d7d00b87cb2a28332ccad4ea4ff676e3f2bc2611 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -693,10 +693,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
 
        for (p = opt->header_list; p; p = p->next) {
                if (p->token != GREP_PATTERN_HEAD)
-                       die("bug: a non-header pattern in grep header list.");
+                       die("BUG: a non-header pattern in grep header list.");
                if (p->field < GREP_HEADER_FIELD_MIN ||
                    GREP_HEADER_FIELD_MAX <= p->field)
-                       die("bug: unknown header field %d", p->field);
+                       die("BUG: unknown header field %d", p->field);
                compile_regexp(p, opt);
        }
 
@@ -709,7 +709,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
 
                h = compile_pattern_atom(&pp);
                if (!h || pp != p->next)
-                       die("bug: malformed header expr");
+                       die("BUG: malformed header expr");
                if (!header_group[p->field]) {
                        header_group[p->field] = h;
                        continue;
@@ -1514,7 +1514,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                case GREP_BINARY_TEXT:
                        break;
                default:
-                       die("bug: unknown binary handling mode");
+                       die("BUG: unknown binary handling mode");
                }
        }
 
index dacada9094efd0c929319f39ca74a908668863c7..704b1c837c9feaa1215e5fd9767c04275a4c1beb 100644 (file)
@@ -1137,7 +1137,7 @@ static void remote_ls(const char *path, int flags,
        ls.userData = userData;
        ls.userFunc = userFunc;
 
-       strbuf_addf(&out_buffer.buf, PROPFIND_ALL_REQUEST);
+       strbuf_addstr(&out_buffer.buf, PROPFIND_ALL_REQUEST);
 
        dav_headers = curl_slist_append(dav_headers, "Depth: 1");
        dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
diff --git a/http.c b/http.c
index e81dd132d483aa02540bac7d1f55b5e98b1ad736..cd40b012f89397db67e851101ab38c2c57fd18c9 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1225,7 +1225,7 @@ void append_remote_object_url(struct strbuf *buf, const char *url,
 
        strbuf_addf(buf, "objects/%.*s/", 2, hex);
        if (!only_two_digit_prefix)
-               strbuf_addf(buf, "%s", hex+2);
+               strbuf_addstr(buf, hex + 2);
 }
 
 char *get_remote_object_url(const char *url, const char *hex,
diff --git a/ident.c b/ident.c
index 139c5289d03b7594af2a07e6e0364bb285ae0348..e20a772dde4230b0871ffe85a5204919402aaf94 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -184,6 +184,11 @@ static const char *ident_default_date(void)
        return git_default_date.buf;
 }
 
+void reset_ident_date(void)
+{
+       strbuf_reset(&git_default_date);
+}
+
 static int crud(unsigned char c)
 {
        return  c <= 32  ||
index db0fafee995874824f91cb8378399fcf720d705c..0f5f4760e90de236757924683478c7f25cdb653f 100644 (file)
@@ -511,7 +511,7 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
 
        va_start(va, fmt);
        if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
-               die("Fatal: buffer too small. Please report a bug.");
+               die("BUG: buffer too small. Please report a bug.");
        va_end(va);
        return ret;
 }
index d0062e6788e95620bb6e1a534661dbb70bdbb098..bfb735c84556f0c6f4d3f17dc4ed18774b8db817 100644 (file)
@@ -263,8 +263,6 @@ void format_decorations_extended(struct strbuf *sb,
 
                        if (current_and_HEAD &&
                            decoration->type == DECORATION_REF_HEAD) {
-                               strbuf_addstr(sb, color_reset);
-                               strbuf_addstr(sb, color_commit);
                                strbuf_addstr(sb, " -> ");
                                strbuf_addstr(sb, color_reset);
                                strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type));
index a4a1195f61ea8ad8186726df1808a85b79415854..c9e4dbc349243caed51ab524edf3d3ebb31a0d2c 100644 (file)
 #include "dir.h"
 #include "submodule.h"
 
+static void flush_output(struct merge_options *o)
+{
+       if (o->buffer_output < 2 && o->obuf.len) {
+               fputs(o->obuf.buf, stdout);
+               strbuf_reset(&o->obuf);
+       }
+}
+
+static int err(struct merge_options *o, const char *err, ...)
+{
+       va_list params;
+
+       if (o->buffer_output < 2)
+               flush_output(o);
+       else {
+               strbuf_complete(&o->obuf, '\n');
+               strbuf_addstr(&o->obuf, "error: ");
+       }
+       va_start(params, err);
+       strbuf_vaddf(&o->obuf, err, params);
+       va_end(params);
+       if (o->buffer_output > 1)
+               strbuf_addch(&o->obuf, '\n');
+       else {
+               error("%s", o->obuf.buf);
+               strbuf_reset(&o->obuf);
+       }
+
+       return -1;
+}
+
 static struct tree *shift_tree_object(struct tree *one, struct tree *two,
                                      const char *subtree_shift)
 {
@@ -148,14 +179,6 @@ static int show(struct merge_options *o, int v)
        return (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;
 }
 
-static void flush_output(struct merge_options *o)
-{
-       if (o->obuf.len) {
-               fputs(o->obuf.buf, stdout);
-               strbuf_reset(&o->obuf);
-       }
-}
-
 __attribute__((format (printf, 3, 4)))
 static void output(struct merge_options *o, int v, const char *fmt, ...)
 {
@@ -177,28 +200,30 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)
 
 static void output_commit_title(struct merge_options *o, struct commit *commit)
 {
-       int i;
-       flush_output(o);
-       for (i = o->call_depth; i--;)
-               fputs("  ", stdout);
+       strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
        if (commit->util)
-               printf("virtual %s\n", merge_remote_util(commit)->name);
+               strbuf_addf(&o->obuf, "virtual %s\n",
+                       merge_remote_util(commit)->name);
        else {
-               printf("%s ", find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
+               strbuf_addf(&o->obuf, "%s ",
+                       find_unique_abbrev(commit->object.oid.hash,
+                               DEFAULT_ABBREV));
                if (parse_commit(commit) != 0)
-                       printf(_("(bad commit)\n"));
+                       strbuf_addf(&o->obuf, _("(bad commit)\n"));
                else {
                        const char *title;
                        const char *msg = get_commit_buffer(commit, NULL);
                        int len = find_commit_subject(msg, &title);
                        if (len)
-                               printf("%.*s\n", len, title);
+                               strbuf_addf(&o->obuf, "%.*s\n", len, title);
                        unuse_commit_buffer(commit, msg);
                }
        }
+       flush_output(o);
 }
 
-static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
+static int add_cacheinfo(struct merge_options *o,
+               unsigned int mode, const struct object_id *oid,
                const char *path, int stage, int refresh, int options)
 {
        struct cache_entry *ce;
@@ -206,7 +231,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
 
        ce = make_cache_entry(mode, oid ? oid->hash : null_sha1, path, stage, 0);
        if (!ce)
-               return error(_("addinfo_cache failed for path '%s'"), path);
+               return err(o, _("addinfo_cache failed for path '%s'"), path);
 
        ret = add_cache_entry(ce, options);
        if (refresh) {
@@ -268,15 +293,17 @@ struct tree *write_tree_from_memory(struct merge_options *o)
                                fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
                                        (int)ce_namelen(ce), ce->name);
                }
-               die("Bug in merge-recursive.c");
+               die("BUG: unmerged index entries in merge-recursive.c");
        }
 
        if (!active_cache_tree)
                active_cache_tree = cache_tree();
 
        if (!cache_tree_fully_valid(active_cache_tree) &&
-           cache_tree_update(&the_index, 0) < 0)
-               die(_("error building trees"));
+           cache_tree_update(&the_index, 0) < 0) {
+               err(o, _("error building trees"));
+               return NULL;
+       }
 
        result = lookup_tree(active_cache_tree->sha1);
 
@@ -542,7 +569,8 @@ static struct string_list *get_renames(struct merge_options *o,
        return renames;
 }
 
-static int update_stages(const char *path, const struct diff_filespec *o,
+static int update_stages(struct merge_options *opt, const char *path,
+                        const struct diff_filespec *o,
                         const struct diff_filespec *a,
                         const struct diff_filespec *b)
 {
@@ -561,13 +589,13 @@ static int update_stages(const char *path, const struct diff_filespec *o,
                if (remove_file_from_cache(path))
                        return -1;
        if (o)
-               if (add_cacheinfo(o->mode, &o->oid, path, 1, 0, options))
+               if (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options))
                        return -1;
        if (a)
-               if (add_cacheinfo(a->mode, &a->oid, path, 2, 0, options))
+               if (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options))
                        return -1;
        if (b)
-               if (add_cacheinfo(b->mode, &b->oid, path, 3, 0, options))
+               if (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options))
                        return -1;
        return 0;
 }
@@ -667,23 +695,21 @@ static int was_tracked(const char *path)
 {
        int pos = cache_name_pos(path, strlen(path));
 
-       if (pos < 0)
-               pos = -1 - pos;
-       while (pos < active_nr &&
-              !strcmp(path, active_cache[pos]->name)) {
-               /*
-                * If stage #0, it is definitely tracked.
-                * If it has stage #2 then it was tracked
-                * before this merge started.  All other
-                * cases the path was not tracked.
-                */
-               switch (ce_stage(active_cache[pos])) {
-               case 0:
-               case 2:
+       if (0 <= pos)
+               /* we have been tracking this path */
+               return 1;
+
+       /*
+        * Look for an unmerged entry for the path,
+        * specifically stage #2, which would indicate
+        * that "our" side before the merge started
+        * had the path tracked (and resulted in a conflict).
+        */
+       for (pos = -1 - pos;
+            pos < active_nr && !strcmp(path, active_cache[pos]->name);
+            pos++)
+               if (ce_stage(active_cache[pos]) == 2)
                        return 1;
-               }
-               pos++;
-       }
        return 0;
 }
 
@@ -718,12 +744,10 @@ static int make_room_for_path(struct merge_options *o, const char *path)
        /* Make sure leading directories are created */
        status = safe_create_leading_directories_const(path);
        if (status) {
-               if (status == SCLD_EXISTS) {
+               if (status == SCLD_EXISTS)
                        /* something else exists */
-                       error(msg, path, _(": perhaps a D/F conflict?"));
-                       return -1;
-               }
-               die(msg, path, "");
+                       return err(o, msg, path, _(": perhaps a D/F conflict?"));
+               return err(o, msg, path, "");
        }
 
        /*
@@ -731,7 +755,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)
         * tracking it.
         */
        if (would_lose_untracked(path))
-               return error(_("refusing to lose untracked file at '%s'"),
+               return err(o, _("refusing to lose untracked file at '%s'"),
                             path);
 
        /* Successful unlink is good.. */
@@ -741,16 +765,18 @@ static int make_room_for_path(struct merge_options *o, const char *path)
        if (errno == ENOENT)
                return 0;
        /* .. but not some other error (who really cares what?) */
-       return error(msg, path, _(": perhaps a D/F conflict?"));
+       return err(o, msg, path, _(": perhaps a D/F conflict?"));
 }
 
-static void update_file_flags(struct merge_options *o,
-                             const struct object_id *oid,
-                             unsigned mode,
-                             const char *path,
-                             int update_cache,
-                             int update_wd)
+static int update_file_flags(struct merge_options *o,
+                            const struct object_id *oid,
+                            unsigned mode,
+                            const char *path,
+                            int update_cache,
+                            int update_wd)
 {
+       int ret = 0;
+
        if (o->call_depth)
                update_wd = 0;
 
@@ -771,9 +797,11 @@ static void update_file_flags(struct merge_options *o,
 
                buf = read_sha1_file(oid->hash, &type, &size);
                if (!buf)
-                       die(_("cannot read object %s '%s'"), oid_to_hex(oid), path);
-               if (type != OBJ_BLOB)
-                       die(_("blob expected for %s '%s'"), oid_to_hex(oid), path);
+                       return err(o, _("cannot read object %s '%s'"), oid_to_hex(oid), path);
+               if (type != OBJ_BLOB) {
+                       ret = err(o, _("blob expected for %s '%s'"), oid_to_hex(oid), path);
+                       goto free_buf;
+               }
                if (S_ISREG(mode)) {
                        struct strbuf strbuf = STRBUF_INIT;
                        if (convert_to_working_tree(path, buf, size, &strbuf)) {
@@ -785,8 +813,7 @@ static void update_file_flags(struct merge_options *o,
 
                if (make_room_for_path(o, path) < 0) {
                        update_wd = 0;
-                       free(buf);
-                       goto update_index;
+                       goto free_buf;
                }
                if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
                        int fd;
@@ -795,8 +822,11 @@ static void update_file_flags(struct merge_options *o,
                        else
                                mode = 0666;
                        fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
-                       if (fd < 0)
-                               die_errno(_("failed to open '%s'"), path);
+                       if (fd < 0) {
+                               ret = err(o, _("failed to open '%s': %s"),
+                                         path, strerror(errno));
+                               goto free_buf;
+                       }
                        write_in_full(fd, buf, size);
                        close(fd);
                } else if (S_ISLNK(mode)) {
@@ -804,25 +834,29 @@ static void update_file_flags(struct merge_options *o,
                        safe_create_leading_directories_const(path);
                        unlink(path);
                        if (symlink(lnk, path))
-                               die_errno(_("failed to symlink '%s'"), path);
+                               ret = err(o, _("failed to symlink '%s': %s"),
+                                       path, strerror(errno));
                        free(lnk);
                } else
-                       die(_("do not know what to do with %06o %s '%s'"),
-                           mode, oid_to_hex(oid), path);
+                       ret = err(o,
+                                 _("do not know what to do with %06o %s '%s'"),
+                                 mode, oid_to_hex(oid), path);
+ free_buf:
                free(buf);
        }
  update_index:
-       if (update_cache)
-               add_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);
+       if (!ret && update_cache)
+               add_cacheinfo(o, mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);
+       return ret;
 }
 
-static void update_file(struct merge_options *o,
-                       int clean,
-                       const struct object_id *oid,
-                       unsigned mode,
-                       const char *path)
+static int update_file(struct merge_options *o,
+                      int clean,
+                      const struct object_id *oid,
+                      unsigned mode,
+                      const char *path)
 {
-       update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);
+       return update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);
 }
 
 /* Low level file merging, update and removal */
@@ -896,117 +930,120 @@ static int merge_3way(struct merge_options *o,
        return merge_status;
 }
 
-static struct merge_file_info merge_file_1(struct merge_options *o,
+static int merge_file_1(struct merge_options *o,
                                           const struct diff_filespec *one,
                                           const struct diff_filespec *a,
                                           const struct diff_filespec *b,
                                           const char *branch1,
-                                          const char *branch2)
+                                          const char *branch2,
+                                          struct merge_file_info *result)
 {
-       struct merge_file_info result;
-       result.merge = 0;
-       result.clean = 1;
+       result->merge = 0;
+       result->clean = 1;
 
        if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {
-               result.clean = 0;
+               result->clean = 0;
                if (S_ISREG(a->mode)) {
-                       result.mode = a->mode;
-                       oidcpy(&result.oid, &a->oid);
+                       result->mode = a->mode;
+                       oidcpy(&result->oid, &a->oid);
                } else {
-                       result.mode = b->mode;
-                       oidcpy(&result.oid, &b->oid);
+                       result->mode = b->mode;
+                       oidcpy(&result->oid, &b->oid);
                }
        } else {
                if (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid))
-                       result.merge = 1;
+                       result->merge = 1;
 
                /*
                 * Merge modes
                 */
                if (a->mode == b->mode || a->mode == one->mode)
-                       result.mode = b->mode;
+                       result->mode = b->mode;
                else {
-                       result.mode = a->mode;
+                       result->mode = a->mode;
                        if (b->mode != one->mode) {
-                               result.clean = 0;
-                               result.merge = 1;
+                               result->clean = 0;
+                               result->merge = 1;
                        }
                }
 
                if (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid))
-                       oidcpy(&result.oid, &b->oid);
+                       oidcpy(&result->oid, &b->oid);
                else if (oid_eq(&b->oid, &one->oid))
-                       oidcpy(&result.oid, &a->oid);
+                       oidcpy(&result->oid, &a->oid);
                else if (S_ISREG(a->mode)) {
                        mmbuffer_t result_buf;
-                       int merge_status;
+                       int ret = 0, merge_status;
 
                        merge_status = merge_3way(o, &result_buf, one, a, b,
                                                  branch1, branch2);
 
                        if ((merge_status < 0) || !result_buf.ptr)
-                               die(_("Failed to execute internal merge"));
+                               ret = err(o, _("Failed to execute internal merge"));
 
-                       if (write_sha1_file(result_buf.ptr, result_buf.size,
-                                           blob_type, result.oid.hash))
-                               die(_("Unable to add %s to database"),
-                                   a->path);
+                       if (!ret && write_sha1_file(result_buf.ptr, result_buf.size,
+                                                   blob_type, result->oid.hash))
+                               ret = err(o, _("Unable to add %s to database"),
+                                         a->path);
 
                        free(result_buf.ptr);
-                       result.clean = (merge_status == 0);
+                       if (ret)
+                               return ret;
+                       result->clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
-                       result.clean = merge_submodule(result.oid.hash,
+                       result->clean = merge_submodule(result->oid.hash,
                                                       one->path,
                                                       one->oid.hash,
                                                       a->oid.hash,
                                                       b->oid.hash,
                                                       !o->call_depth);
                } else if (S_ISLNK(a->mode)) {
-                       oidcpy(&result.oid, &a->oid);
+                       oidcpy(&result->oid, &a->oid);
 
                        if (!oid_eq(&a->oid, &b->oid))
-                               result.clean = 0;
-               } else {
-                       die(_("unsupported object type in the tree"));
-               }
+                               result->clean = 0;
+               } else
+                       die("BUG: unsupported object type in the tree");
        }
 
-       return result;
+       return 0;
 }
 
-static struct merge_file_info
-merge_file_special_markers(struct merge_options *o,
+static int merge_file_special_markers(struct merge_options *o,
                           const struct diff_filespec *one,
                           const struct diff_filespec *a,
                           const struct diff_filespec *b,
                           const char *branch1,
                           const char *filename1,
                           const char *branch2,
-                          const char *filename2)
+                          const char *filename2,
+                          struct merge_file_info *mfi)
 {
        char *side1 = NULL;
        char *side2 = NULL;
-       struct merge_file_info mfi;
+       int ret;
 
        if (filename1)
                side1 = xstrfmt("%s:%s", branch1, filename1);
        if (filename2)
                side2 = xstrfmt("%s:%s", branch2, filename2);
 
-       mfi = merge_file_1(o, one, a, b,
-                          side1 ? side1 : branch1, side2 ? side2 : branch2);
+       ret = merge_file_1(o, one, a, b,
+                          side1 ? side1 : branch1,
+                          side2 ? side2 : branch2, mfi);
        free(side1);
        free(side2);
-       return mfi;
+       return ret;
 }
 
-static struct merge_file_info merge_file_one(struct merge_options *o,
+static int merge_file_one(struct merge_options *o,
                                         const char *path,
                                         const struct object_id *o_oid, int o_mode,
                                         const struct object_id *a_oid, int a_mode,
                                         const struct object_id *b_oid, int b_mode,
                                         const char *branch1,
-                                        const char *branch2)
+                                        const char *branch2,
+                                        struct merge_file_info *mfi)
 {
        struct diff_filespec one, a, b;
 
@@ -1017,10 +1054,10 @@ static struct merge_file_info merge_file_one(struct merge_options *o,
        a.mode = a_mode;
        oidcpy(&b.oid, b_oid);
        b.mode = b_mode;
-       return merge_file_1(o, &one, &a, &b, branch1, branch2);
+       return merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);
 }
 
-static void handle_change_delete(struct merge_options *o,
+static int handle_change_delete(struct merge_options *o,
                                 const char *path,
                                 const struct object_id *o_oid, int o_mode,
                                 const struct object_id *a_oid, int a_mode,
@@ -1028,6 +1065,7 @@ static void handle_change_delete(struct merge_options *o,
                                 const char *change, const char *change_past)
 {
        char *renamed = NULL;
+       int ret = 0;
        if (dir_in_way(path, !o->call_depth)) {
                renamed = unique_path(o, path, a_oid ? o->branch1 : o->branch2);
        }
@@ -1038,21 +1076,23 @@ static void handle_change_delete(struct merge_options *o,
                 * correct; since there is no true "middle point" between
                 * them, simply reuse the base version for virtual merge base.
                 */
-               remove_file_from_cache(path);
-               update_file(o, 0, o_oid, o_mode, renamed ? renamed : path);
+               ret = remove_file_from_cache(path);
+               if (!ret)
+                       ret = update_file(o, 0, o_oid, o_mode,
+                                         renamed ? renamed : path);
        } else if (!a_oid) {
                if (!renamed) {
                        output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
                               "and %s in %s. Version %s of %s left in tree."),
                               change, path, o->branch1, change_past,
                               o->branch2, o->branch2, path);
-                       update_file(o, 0, b_oid, b_mode, path);
+                       ret = update_file(o, 0, b_oid, b_mode, path);
                } else {
                        output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
                               "and %s in %s. Version %s of %s left in tree at %s."),
                               change, path, o->branch1, change_past,
                               o->branch2, o->branch2, path, renamed);
-                       update_file(o, 0, b_oid, b_mode, renamed);
+                       ret = update_file(o, 0, b_oid, b_mode, renamed);
                }
        } else {
                if (!renamed) {
@@ -1065,7 +1105,7 @@ static void handle_change_delete(struct merge_options *o,
                               "and %s in %s. Version %s of %s left in tree at %s."),
                               change, path, o->branch2, change_past,
                               o->branch1, o->branch1, path, renamed);
-                       update_file(o, 0, a_oid, a_mode, renamed);
+                       ret = update_file(o, 0, a_oid, a_mode, renamed);
                }
                /*
                 * No need to call update_file() on path when !renamed, since
@@ -1075,9 +1115,11 @@ static void handle_change_delete(struct merge_options *o,
                 */
        }
        free(renamed);
+
+       return ret;
 }
 
-static void conflict_rename_delete(struct merge_options *o,
+static int conflict_rename_delete(struct merge_options *o,
                                   struct diff_filepair *pair,
                                   const char *rename_branch,
                                   const char *other_branch)
@@ -1097,21 +1139,20 @@ static void conflict_rename_delete(struct merge_options *o,
                b_mode = dest->mode;
        }
 
-       handle_change_delete(o,
-                            o->call_depth ? orig->path : dest->path,
-                            &orig->oid, orig->mode,
-                            a_oid, a_mode,
-                            b_oid, b_mode,
-                            _("rename"), _("renamed"));
-
-       if (o->call_depth) {
-               remove_file_from_cache(dest->path);
-       } else {
-               update_stages(dest->path, NULL,
-                             rename_branch == o->branch1 ? dest : NULL,
-                             rename_branch == o->branch1 ? NULL : dest);
-       }
+       if (handle_change_delete(o,
+                                o->call_depth ? orig->path : dest->path,
+                                &orig->oid, orig->mode,
+                                a_oid, a_mode,
+                                b_oid, b_mode,
+                                _("rename"), _("renamed")))
+               return -1;
 
+       if (o->call_depth)
+               return remove_file_from_cache(dest->path);
+       else
+               return update_stages(o, dest->path, NULL,
+                                    rename_branch == o->branch1 ? dest : NULL,
+                                    rename_branch == o->branch1 ? NULL : dest);
 }
 
 static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,
@@ -1127,7 +1168,7 @@ static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,
        return target;
 }
 
-static void handle_file(struct merge_options *o,
+static int handle_file(struct merge_options *o,
                        struct diff_filespec *rename,
                        int stage,
                        struct rename_conflict_info *ci)
@@ -1137,6 +1178,7 @@ static void handle_file(struct merge_options *o,
        const char *cur_branch, *other_branch;
        struct diff_filespec other;
        struct diff_filespec *add;
+       int ret;
 
        if (stage == 2) {
                dst_entry = ci->dst_entry1;
@@ -1151,7 +1193,8 @@ static void handle_file(struct merge_options *o,
        add = filespec_from_entry(&other, dst_entry, stage ^ 1);
        if (add) {
                char *add_name = unique_path(o, rename->path, other_branch);
-               update_file(o, 0, &add->oid, add->mode, add_name);
+               if (update_file(o, 0, &add->oid, add->mode, add_name))
+                       return -1;
 
                remove_file(o, 0, rename->path, 0);
                dst_name = unique_path(o, rename->path, cur_branch);
@@ -1162,17 +1205,20 @@ static void handle_file(struct merge_options *o,
                               rename->path, other_branch, dst_name);
                }
        }
-       update_file(o, 0, &rename->oid, rename->mode, dst_name);
-       if (stage == 2)
-               update_stages(rename->path, NULL, rename, add);
+       if ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))
+               ; /* fall through, do allow dst_name to be released */
+       else if (stage == 2)
+               ret = update_stages(o, rename->path, NULL, rename, add);
        else
-               update_stages(rename->path, NULL, add, rename);
+               ret = update_stages(o, rename->path, NULL, add, rename);
 
        if (dst_name != rename->path)
                free(dst_name);
+
+       return ret;
 }
 
-static void conflict_rename_rename_1to2(struct merge_options *o,
+static int conflict_rename_rename_1to2(struct merge_options *o,
                                        struct rename_conflict_info *ci)
 {
        /* One file was renamed in both branches, but to different names. */
@@ -1190,18 +1236,21 @@ static void conflict_rename_rename_1to2(struct merge_options *o,
                struct merge_file_info mfi;
                struct diff_filespec other;
                struct diff_filespec *add;
-               mfi = merge_file_one(o, one->path,
+               if (merge_file_one(o, one->path,
                                 &one->oid, one->mode,
                                 &a->oid, a->mode,
                                 &b->oid, b->mode,
-                                ci->branch1, ci->branch2);
+                                ci->branch1, ci->branch2, &mfi))
+                       return -1;
+
                /*
                 * FIXME: For rename/add-source conflicts (if we could detect
                 * such), this is wrong.  We should instead find a unique
                 * pathname and then either rename the add-source file to that
                 * unique path, or use that unique path instead of src here.
                 */
-               update_file(o, 0, &mfi.oid, mfi.mode, one->path);
+               if (update_file(o, 0, &mfi.oid, mfi.mode, one->path))
+                       return -1;
 
                /*
                 * Above, we put the merged content at the merge-base's
@@ -1212,22 +1261,26 @@ static void conflict_rename_rename_1to2(struct merge_options *o,
                 * resolving the conflict at that path in its favor.
                 */
                add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);
-               if (add)
-                       update_file(o, 0, &add->oid, add->mode, a->path);
+               if (add) {
+                       if (update_file(o, 0, &add->oid, add->mode, a->path))
+                               return -1;
+               }
                else
                        remove_file_from_cache(a->path);
                add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);
-               if (add)
-                       update_file(o, 0, &add->oid, add->mode, b->path);
+               if (add) {
+                       if (update_file(o, 0, &add->oid, add->mode, b->path))
+                               return -1;
+               }
                else
                        remove_file_from_cache(b->path);
-       } else {
-               handle_file(o, a, 2, ci);
-               handle_file(o, b, 3, ci);
-       }
+       } else if (handle_file(o, a, 2, ci) || handle_file(o, b, 3, ci))
+               return -1;
+
+       return 0;
 }
 
-static void conflict_rename_rename_2to1(struct merge_options *o,
+static int conflict_rename_rename_2to1(struct merge_options *o,
                                        struct rename_conflict_info *ci)
 {
        /* Two files, a & b, were renamed to the same thing, c. */
@@ -1238,6 +1291,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,
        char *path = c1->path; /* == c2->path */
        struct merge_file_info mfi_c1;
        struct merge_file_info mfi_c2;
+       int ret;
 
        output(o, 1, _("CONFLICT (rename/rename): "
               "Rename %s->%s in %s. "
@@ -1248,12 +1302,13 @@ static void conflict_rename_rename_2to1(struct merge_options *o,
        remove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));
        remove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));
 
-       mfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,
-                                           o->branch1, c1->path,
-                                           o->branch2, ci->ren1_other.path);
-       mfi_c2 = merge_file_special_markers(o, b, &ci->ren2_other, c2,
-                                           o->branch1, ci->ren2_other.path,
-                                           o->branch2, c2->path);
+       if (merge_file_special_markers(o, a, c1, &ci->ren1_other,
+                                      o->branch1, c1->path,
+                                      o->branch2, ci->ren1_other.path, &mfi_c1) ||
+           merge_file_special_markers(o, b, &ci->ren2_other, c2,
+                                      o->branch1, ci->ren2_other.path,
+                                      o->branch2, c2->path, &mfi_c2))
+               return -1;
 
        if (o->call_depth) {
                /*
@@ -1264,19 +1319,25 @@ static void conflict_rename_rename_2to1(struct merge_options *o,
                 * again later for the non-recursive merge.
                 */
                remove_file(o, 0, path, 0);
-               update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);
-               update_file(o, 0, &mfi_c2.oid, mfi_c2.mode, b->path);
+               ret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);
+               if (!ret)
+                       ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,
+                                         b->path);
        } else {
                char *new_path1 = unique_path(o, path, ci->branch1);
                char *new_path2 = unique_path(o, path, ci->branch2);
                output(o, 1, _("Renaming %s to %s and %s to %s instead"),
                       a->path, new_path1, b->path, new_path2);
                remove_file(o, 0, path, 0);
-               update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);
-               update_file(o, 0, &mfi_c2.oid, mfi_c2.mode, new_path2);
+               ret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);
+               if (!ret)
+                       ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,
+                                         new_path2);
                free(new_path2);
                free(new_path1);
        }
+
+       return ret;
 }
 
 static int process_renames(struct merge_options *o,
@@ -1354,7 +1415,7 @@ static int process_renames(struct merge_options *o,
                        const char *ren2_dst = ren2->pair->two->path;
                        enum rename_type rename_type;
                        if (strcmp(ren1_src, ren2_src) != 0)
-                               die("ren1_src != ren2_src");
+                               die("BUG: ren1_src != ren2_src");
                        ren2->dst_entry->processed = 1;
                        ren2->processed = 1;
                        if (strcmp(ren1_dst, ren2_dst) != 0) {
@@ -1388,7 +1449,7 @@ static int process_renames(struct merge_options *o,
                        ren2 = lookup->util;
                        ren2_dst = ren2->pair->two->path;
                        if (strcmp(ren1_dst, ren2_dst) != 0)
-                               die("ren1_dst != ren2_dst");
+                               die("BUG: ren1_dst != ren2_dst");
 
                        clean_merge = 0;
                        ren2->processed = 1;
@@ -1461,12 +1522,13 @@ static int process_renames(struct merge_options *o,
                                 * update_file_flags() instead of
                                 * update_file().
                                 */
-                               update_file_flags(o,
-                                                 &ren1->pair->two->oid,
-                                                 ren1->pair->two->mode,
-                                                 ren1_dst,
-                                                 1, /* update_cache */
-                                                 0  /* update_wd    */);
+                               if (update_file_flags(o,
+                                                     &ren1->pair->two->oid,
+                                                     ren1->pair->two->mode,
+                                                     ren1_dst,
+                                                     1, /* update_cache */
+                                                     0  /* update_wd    */))
+                                       clean_merge = -1;
                        } else if (!oid_eq(&dst_other.oid, &null_oid)) {
                                clean_merge = 0;
                                try_merge = 1;
@@ -1476,26 +1538,33 @@ static int process_renames(struct merge_options *o,
                                       ren1_dst, branch2);
                                if (o->call_depth) {
                                        struct merge_file_info mfi;
-                                       mfi = merge_file_one(o, ren1_dst, &null_oid, 0,
-                                                        &ren1->pair->two->oid,
-                                                        ren1->pair->two->mode,
-                                                        &dst_other.oid,
-                                                        dst_other.mode,
-                                                        branch1, branch2);
+                                       if (merge_file_one(o, ren1_dst, &null_oid, 0,
+                                                          &ren1->pair->two->oid,
+                                                          ren1->pair->two->mode,
+                                                          &dst_other.oid,
+                                                          dst_other.mode,
+                                                          branch1, branch2, &mfi)) {
+                                               clean_merge = -1;
+                                               goto cleanup_and_return;
+                                       }
                                        output(o, 1, _("Adding merged %s"), ren1_dst);
-                                       update_file(o, 0, &mfi.oid,
-                                                   mfi.mode, ren1_dst);
+                                       if (update_file(o, 0, &mfi.oid,
+                                                       mfi.mode, ren1_dst))
+                                               clean_merge = -1;
                                        try_merge = 0;
                                } else {
                                        char *new_path = unique_path(o, ren1_dst, branch2);
                                        output(o, 1, _("Adding as %s instead"), new_path);
-                                       update_file(o, 0, &dst_other.oid,
-                                                   dst_other.mode, new_path);
+                                       if (update_file(o, 0, &dst_other.oid,
+                                                       dst_other.mode, new_path))
+                                               clean_merge = -1;
                                        free(new_path);
                                }
                        } else
                                try_merge = 1;
 
+                       if (clean_merge < 0)
+                               goto cleanup_and_return;
                        if (try_merge) {
                                struct diff_filespec *one, *a, *b;
                                src_other.path = (char *)ren1_src;
@@ -1522,6 +1591,7 @@ static int process_renames(struct merge_options *o,
                        }
                }
        }
+cleanup_and_return:
        string_list_clear(&a_by_dst, 0);
        string_list_clear(&b_by_dst, 0);
 
@@ -1533,23 +1603,25 @@ static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)
        return (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;
 }
 
-static int read_oid_strbuf(const struct object_id *oid, struct strbuf *dst)
+static int read_oid_strbuf(struct merge_options *o,
+       const struct object_id *oid, struct strbuf *dst)
 {
        void *buf;
        enum object_type type;
        unsigned long size;
        buf = read_sha1_file(oid->hash, &type, &size);
        if (!buf)
-               return error(_("cannot read object %s"), oid_to_hex(oid));
+               return err(o, _("cannot read object %s"), oid_to_hex(oid));
        if (type != OBJ_BLOB) {
                free(buf);
-               return error(_("object %s is not a blob"), oid_to_hex(oid));
+               return err(o, _("object %s is not a blob"), oid_to_hex(oid));
        }
        strbuf_attach(dst, buf, size, size + 1);
        return 0;
 }
 
-static int blob_unchanged(const struct object_id *o_oid,
+static int blob_unchanged(struct merge_options *opt,
+                         const struct object_id *o_oid,
                          unsigned o_mode,
                          const struct object_id *a_oid,
                          unsigned a_mode,
@@ -1567,7 +1639,7 @@ static int blob_unchanged(const struct object_id *o_oid,
                return 0;
 
        assert(o_oid && a_oid);
-       if (read_oid_strbuf(o_oid, &o) || read_oid_strbuf(a_oid, &a))
+       if (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a))
                goto error_return;
        /*
         * Note: binary | is used so that both renormalizations are
@@ -1584,18 +1656,18 @@ static int blob_unchanged(const struct object_id *o_oid,
        return ret;
 }
 
-static void handle_modify_delete(struct merge_options *o,
+static int handle_modify_delete(struct merge_options *o,
                                 const char *path,
                                 struct object_id *o_oid, int o_mode,
                                 struct object_id *a_oid, int a_mode,
                                 struct object_id *b_oid, int b_mode)
 {
-       handle_change_delete(o,
-                            path,
-                            o_oid, o_mode,
-                            a_oid, a_mode,
-                            b_oid, b_mode,
-                            _("modify"), _("modified"));
+       return handle_change_delete(o,
+                                   path,
+                                   o_oid, o_mode,
+                                   a_oid, a_mode,
+                                   b_oid, b_mode,
+                                   _("modify"), _("modified"));
 }
 
 static int merge_content(struct merge_options *o,
@@ -1639,9 +1711,10 @@ static int merge_content(struct merge_options *o,
                if (dir_in_way(path, !o->call_depth))
                        df_conflict_remains = 1;
        }
-       mfi = merge_file_special_markers(o, &one, &a, &b,
-                                        o->branch1, path1,
-                                        o->branch2, path2);
+       if (merge_file_special_markers(o, &one, &a, &b,
+                                      o->branch1, path1,
+                                      o->branch2, path2, &mfi))
+               return -1;
 
        if (mfi.clean && !df_conflict_remains &&
            oid_eq(&mfi.oid, a_oid) && mfi.mode == a_mode) {
@@ -1655,7 +1728,7 @@ static int merge_content(struct merge_options *o,
                 */
                path_renamed_outside_HEAD = !path2 || !strcmp(path, path2);
                if (!path_renamed_outside_HEAD) {
-                       add_cacheinfo(mfi.mode, &mfi.oid, path,
+                       add_cacheinfo(o, mfi.mode, &mfi.oid, path,
                                      0, (!o->call_depth), 0);
                        return mfi.clean;
                }
@@ -1668,7 +1741,8 @@ static int merge_content(struct merge_options *o,
                output(o, 1, _("CONFLICT (%s): Merge conflict in %s"),
                                reason, path);
                if (rename_conflict_info && !df_conflict_remains)
-                       update_stages(path, &one, &a, &b);
+                       if (update_stages(o, path, &one, &a, &b))
+                               return -1;
        }
 
        if (df_conflict_remains) {
@@ -1676,30 +1750,33 @@ static int merge_content(struct merge_options *o,
                if (o->call_depth) {
                        remove_file_from_cache(path);
                } else {
-                       if (!mfi.clean)
-                               update_stages(path, &one, &a, &b);
-                       else {
+                       if (!mfi.clean) {
+                               if (update_stages(o, path, &one, &a, &b))
+                                       return -1;
+                       } else {
                                int file_from_stage2 = was_tracked(path);
                                struct diff_filespec merged;
                                oidcpy(&merged.oid, &mfi.oid);
                                merged.mode = mfi.mode;
 
-                               update_stages(path, NULL,
-                                             file_from_stage2 ? &merged : NULL,
-                                             file_from_stage2 ? NULL : &merged);
+                               if (update_stages(o, path, NULL,
+                                                 file_from_stage2 ? &merged : NULL,
+                                                 file_from_stage2 ? NULL : &merged))
+                                       return -1;
                        }
 
                }
                new_path = unique_path(o, path, rename_conflict_info->branch1);
                output(o, 1, _("Adding as %s instead"), new_path);
-               update_file(o, 0, &mfi.oid, mfi.mode, new_path);
+               if (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) {
+                       free(new_path);
+                       return -1;
+               }
                free(new_path);
                mfi.clean = 0;
-       } else {
-               update_file(o, mfi.clean, &mfi.oid, mfi.mode, path);
-       }
+       } else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path))
+               return -1;
        return mfi.clean;
-
 }
 
 /* Per entry merge function */
@@ -1727,17 +1804,21 @@ static int process_entry(struct merge_options *o,
                        break;
                case RENAME_DELETE:
                        clean_merge = 0;
-                       conflict_rename_delete(o, conflict_info->pair1,
-                                              conflict_info->branch1,
-                                              conflict_info->branch2);
+                       if (conflict_rename_delete(o,
+                                                  conflict_info->pair1,
+                                                  conflict_info->branch1,
+                                                  conflict_info->branch2))
+                               clean_merge = -1;
                        break;
                case RENAME_ONE_FILE_TO_TWO:
                        clean_merge = 0;
-                       conflict_rename_rename_1to2(o, conflict_info);
+                       if (conflict_rename_rename_1to2(o, conflict_info))
+                               clean_merge = -1;
                        break;
                case RENAME_TWO_FILES_TO_ONE:
                        clean_merge = 0;
-                       conflict_rename_rename_2to1(o, conflict_info);
+                       if (conflict_rename_rename_2to1(o, conflict_info))
+                               clean_merge = -1;
                        break;
                default:
                        entry->processed = 0;
@@ -1746,8 +1827,8 @@ static int process_entry(struct merge_options *o,
        } else if (o_oid && (!a_oid || !b_oid)) {
                /* Case A: Deleted in one */
                if ((!a_oid && !b_oid) ||
-                   (!b_oid && blob_unchanged(o_oid, o_mode, a_oid, a_mode, normalize, path)) ||
-                   (!a_oid && blob_unchanged(o_oid, o_mode, b_oid, b_mode, normalize, path))) {
+                   (!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||
+                   (!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) {
                        /* Deleted in both or deleted in one and
                         * unchanged in the other */
                        if (a_oid)
@@ -1757,8 +1838,9 @@ static int process_entry(struct merge_options *o,
                } else {
                        /* Modify/delete; deleted side may have put a directory in the way */
                        clean_merge = 0;
-                       handle_modify_delete(o, path, o_oid, o_mode,
-                                            a_oid, a_mode, b_oid, b_mode);
+                       if (handle_modify_delete(o, path, o_oid, o_mode,
+                                                a_oid, a_mode, b_oid, b_mode))
+                               clean_merge = -1;
                }
        } else if ((!o_oid && a_oid && !b_oid) ||
                   (!o_oid && !a_oid && b_oid)) {
@@ -1790,14 +1872,16 @@ static int process_entry(struct merge_options *o,
                        output(o, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
                               "Adding %s as %s"),
                               conf, path, other_branch, path, new_path);
-                       update_file(o, 0, oid, mode, new_path);
-                       if (o->call_depth)
+                       if (update_file(o, 0, oid, mode, new_path))
+                               clean_merge = -1;
+                       else if (o->call_depth)
                                remove_file_from_cache(path);
                        free(new_path);
                } else {
                        output(o, 2, _("Adding %s"), path);
                        /* do not overwrite file if already present */
-                       update_file_flags(o, oid, mode, path, 1, !a_oid);
+                       if (update_file_flags(o, oid, mode, path, 1, !a_oid))
+                               clean_merge = -1;
                }
        } else if (a_oid && b_oid) {
                /* Case C: Added in both (check for same permissions) and */
@@ -1812,7 +1896,7 @@ static int process_entry(struct merge_options *o,
                 */
                remove_file(o, 1, path, !a_mode);
        } else
-               die(_("Fatal merge failure, shouldn't happen."));
+               die("BUG: fatal merge failure, shouldn't happen.");
 
        return clean_merge;
 }
@@ -1840,11 +1924,10 @@ int merge_trees(struct merge_options *o,
 
        if (code != 0) {
                if (show(o, 4) || o->call_depth)
-                       die(_("merging of trees %s and %s failed"),
+                       err(o, _("merging of trees %s and %s failed"),
                            oid_to_hex(&head->object.oid),
                            oid_to_hex(&merge->object.oid));
-               else
-                       exit(128);
+               return -1;
        }
 
        if (unmerged_cache()) {
@@ -1860,17 +1943,23 @@ int merge_trees(struct merge_options *o,
                re_head  = get_renames(o, head, common, head, merge, entries);
                re_merge = get_renames(o, merge, common, head, merge, entries);
                clean = process_renames(o, re_head, re_merge);
+               if (clean < 0)
+                       return clean;
                for (i = entries->nr-1; 0 <= i; i--) {
                        const char *path = entries->items[i].string;
                        struct stage_data *e = entries->items[i].util;
-                       if (!e->processed
-                               && !process_entry(o, path, e))
-                               clean = 0;
+                       if (!e->processed) {
+                               int ret = process_entry(o, path, e);
+                               if (!ret)
+                                       clean = 0;
+                               else if (ret < 0)
+                                       return ret;
+                       }
                }
                for (i = 0; i < entries->nr; i++) {
                        struct stage_data *e = entries->items[i].util;
                        if (!e->processed)
-                               die(_("Unprocessed path??? %s"),
+                               die("BUG: unprocessed path??? %s",
                                    entries->items[i].string);
                }
 
@@ -1885,8 +1974,8 @@ int merge_trees(struct merge_options *o,
        else
                clean = 1;
 
-       if (o->call_depth)
-               *result = write_tree_from_memory(o);
+       if (o->call_depth && !(*result = write_tree_from_memory(o)))
+               return -1;
 
        return clean;
 }
@@ -1952,23 +2041,25 @@ int merge_recursive(struct merge_options *o,
                /*
                 * When the merge fails, the result contains files
                 * with conflict markers. The cleanness flag is
-                * ignored, it was never actually used, as result of
-                * merge_trees has always overwritten it: the committed
-                * "conflicts" were already resolved.
+                * ignored (unless indicating an error), it was never
+                * actually used, as result of merge_trees has always
+                * overwritten it: the committed "conflicts" were
+                * already resolved.
                 */
                discard_cache();
                saved_b1 = o->branch1;
                saved_b2 = o->branch2;
                o->branch1 = "Temporary merge branch 1";
                o->branch2 = "Temporary merge branch 2";
-               merge_recursive(o, merged_common_ancestors, iter->item,
-                               NULL, &merged_common_ancestors);
+               if (merge_recursive(o, merged_common_ancestors, iter->item,
+                                   NULL, &merged_common_ancestors) < 0)
+                       return -1;
                o->branch1 = saved_b1;
                o->branch2 = saved_b2;
                o->call_depth--;
 
                if (!merged_common_ancestors)
-                       die(_("merge returned no commit"));
+                       return err(o, _("merge returned no commit"));
        }
 
        discard_cache();
@@ -1978,6 +2069,10 @@ int merge_recursive(struct merge_options *o,
        o->ancestor = "merged common ancestors";
        clean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,
                            &mrtree);
+       if (clean < 0) {
+               flush_output(o);
+               return clean;
+       }
 
        if (o->call_depth) {
                *result = make_virtual_commit(mrtree, "merged tree");
@@ -1985,6 +2080,8 @@ int merge_recursive(struct merge_options *o,
                commit_list_insert(h2, &(*result)->parents->next);
        }
        flush_output(o);
+       if (!o->call_depth && o->buffer_output < 2)
+               strbuf_release(&o->obuf);
        if (show(o, 2))
                diff_warn_rename_limit("merge.renamelimit",
                                       o->needed_rename_limit, 0);
@@ -2025,7 +2122,7 @@ int merge_recursive_generic(struct merge_options *o,
                for (i = 0; i < num_base_list; ++i) {
                        struct commit *base;
                        if (!(base = get_ref(base_list[i], oid_to_hex(base_list[i]))))
-                               return error(_("Could not parse object '%s'"),
+                               return err(o, _("Could not parse object '%s'"),
                                        oid_to_hex(base_list[i]));
                        commit_list_insert(base, &ca);
                }
@@ -2034,9 +2131,12 @@ int merge_recursive_generic(struct merge_options *o,
        hold_locked_index(lock, 1);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
+       if (clean < 0)
+               return clean;
+
        if (active_cache_changed &&
            write_locked_index(&the_index, lock, COMMIT_LOCK))
-               return error(_("Unable to write index."));
+               return err(o, _("Unable to write index."));
 
        return clean ? 0 : 1;
 }
index d415724aea24d0b82f558025a503d5e46df30fc4..735343b4136d998ee3d2ac3672bdd7b5a5ba017e 100644 (file)
@@ -13,7 +13,7 @@ struct merge_options {
                MERGE_RECURSIVE_THEIRS
        } recursive_variant;
        const char *subtree_shift;
-       unsigned buffer_output : 1;
+       unsigned buffer_output; /* 1: output at end, 2: keep buffered */
        unsigned renormalize : 1;
        long xdl_opts;
        int verbosity;
diff --git a/mru.c b/mru.c
new file mode 100644 (file)
index 0000000..9dedae0
--- /dev/null
+++ b/mru.c
@@ -0,0 +1,50 @@
+#include "cache.h"
+#include "mru.h"
+
+void mru_append(struct mru *mru, void *item)
+{
+       struct mru_entry *cur = xmalloc(sizeof(*cur));
+       cur->item = item;
+       cur->prev = mru->tail;
+       cur->next = NULL;
+
+       if (mru->tail)
+               mru->tail->next = cur;
+       else
+               mru->head = cur;
+       mru->tail = cur;
+}
+
+void mru_mark(struct mru *mru, struct mru_entry *entry)
+{
+       /* If we're already at the front of the list, nothing to do */
+       if (mru->head == entry)
+               return;
+
+       /* Otherwise, remove us from our current slot... */
+       if (entry->prev)
+               entry->prev->next = entry->next;
+       if (entry->next)
+               entry->next->prev = entry->prev;
+       else
+               mru->tail = entry->prev;
+
+       /* And insert us at the beginning. */
+       entry->prev = NULL;
+       entry->next = mru->head;
+       if (mru->head)
+               mru->head->prev = entry;
+       mru->head = entry;
+}
+
+void mru_clear(struct mru *mru)
+{
+       struct mru_entry *p = mru->head;
+
+       while (p) {
+               struct mru_entry *to_free = p;
+               p = p->next;
+               free(to_free);
+       }
+       mru->head = mru->tail = NULL;
+}
diff --git a/mru.h b/mru.h
new file mode 100644 (file)
index 0000000..42e4aea
--- /dev/null
+++ b/mru.h
@@ -0,0 +1,45 @@
+#ifndef MRU_H
+#define MRU_H
+
+/**
+ * A simple most-recently-used cache, backed by a doubly-linked list.
+ *
+ * Usage is roughly:
+ *
+ *   // Create a list.  Zero-initialization is required.
+ *   static struct mru cache;
+ *   mru_append(&cache, item);
+ *   ...
+ *
+ *   // Iterate in MRU order.
+ *   struct mru_entry *p;
+ *   for (p = cache.head; p; p = p->next) {
+ *     if (matches(p->item))
+ *             break;
+ *   }
+ *
+ *   // Mark an item as used, moving it to the front of the list.
+ *   mru_mark(&cache, p);
+ *
+ *   // Reset the list to empty, cleaning up all resources.
+ *   mru_clear(&cache);
+ *
+ * Note that you SHOULD NOT call mru_mark() and then continue traversing the
+ * list; it reorders the marked item to the front of the list, and therefore
+ * you will begin traversing the whole list again.
+ */
+
+struct mru_entry {
+       void *item;
+       struct mru_entry *prev, *next;
+};
+
+struct mru {
+       struct mru_entry *head, *tail;
+};
+
+void mru_append(struct mru *mru, void *item);
+void mru_mark(struct mru *mru, struct mru_entry *entry);
+void mru_clear(struct mru *mru);
+
+#endif /* MRU_H */
index 33293ce2a6b8ea51516e42d01001b15a3016ca27..ea0b78813081236401efbf5f102e7f89a7d5ed7b 100644 (file)
@@ -354,7 +354,6 @@ void finish_tmp_packfile(struct strbuf *name_buffer,
                die_errno("unable to make temporary index file readable");
 
        strbuf_addf(name_buffer, "%s.pack", sha1_to_hex(sha1));
-       free_pack_by_name(name_buffer->buf);
 
        if (rename(pack_tmp_name, name_buffer->buf))
                die_errno("unable to rename temporary pack file");
diff --git a/pager.c b/pager.c
index 4bc048148e043eabf1315bbcbae8ea4c6363b330..6470b8180df7e0de51a0d71ac47f8c078d226923 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -63,14 +63,38 @@ const char *git_pager(int stdout_is_tty)
        return pager;
 }
 
+static void setup_pager_env(struct argv_array *env)
+{
+       const char **argv;
+       int i;
+       char *pager_env = xstrdup(PAGER_ENV);
+       int n = split_cmdline(pager_env, &argv);
+
+       if (n < 0)
+               die("malformed build-time PAGER_ENV: %s",
+                       split_cmdline_strerror(n));
+
+       for (i = 0; i < n; i++) {
+               char *cp = strchr(argv[i], '=');
+
+               if (!cp)
+                       die("malformed build-time PAGER_ENV");
+
+               *cp = '\0';
+               if (!getenv(argv[i])) {
+                       *cp = '=';
+                       argv_array_push(env, argv[i]);
+               }
+       }
+       free(pager_env);
+       free(argv);
+}
+
 void prepare_pager_args(struct child_process *pager_process, const char *pager)
 {
        argv_array_push(&pager_process->args, pager);
        pager_process->use_shell = 1;
-       if (!getenv("LESS"))
-               argv_array_push(&pager_process->env_array, "LESS=FRX");
-       if (!getenv("LV"))
-               argv_array_push(&pager_process->env_array, "LV=-c");
+       setup_pager_env(&pager_process->env_array);
 }
 
 void setup_pager(void)
diff --git a/refs.c b/refs.c
index 814cad316384f953be975cdb711e320b4f1290d3..b4e7cac7b26d0903c4ab7361430c38232c1d5e82 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -922,7 +922,7 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                        /* -2 for strlen("%.*s") - strlen("%s"); +1 for NUL */
                        total_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;
 
-               scanf_fmts = xmalloc(st_add(st_mult(nr_rules, sizeof(char *)), total_len));
+               scanf_fmts = xmalloc(st_add(st_mult(sizeof(char *), nr_rules), total_len));
 
                offset = 0;
                for (i = 0; i < nr_rules; i++) {
index a326e4e2516e2129e7a08bfc149786402ec160fb..d29850a81cdb09b41da5dc3e7284106672d85a4c 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1544,8 +1544,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                 * branch.
                 */
                if (ref->expect_old_sha1) {
-                       if (ref->expect_old_no_trackback ||
-                           oidcmp(&ref->old_oid, &ref->old_oid_expect))
+                       if (oidcmp(&ref->old_oid, &ref->old_oid_expect))
                                reject_reason = REF_STATUS_REJECT_STALE;
                        else
                                /* If the ref isn't stale then force the update. */
@@ -2294,6 +2293,8 @@ int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unse
        entry = add_cas_entry(cas, arg, colon - arg);
        if (!*colon)
                entry->use_tracking = 1;
+       else if (!colon[1])
+               hashclr(entry->expect);
        else if (get_sha1(colon + 1, entry->expect))
                return error("cannot parse expected object name '%s'", colon + 1);
        return 0;
@@ -2343,7 +2344,7 @@ static void apply_cas(struct push_cas_option *cas,
                if (!entry->use_tracking)
                        hashcpy(ref->old_oid_expect.hash, cas->entry[i].expect);
                else if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
-                       ref->expect_old_no_trackback = 1;
+                       oidclr(&ref->old_oid_expect);
                return;
        }
 
@@ -2353,7 +2354,7 @@ static void apply_cas(struct push_cas_option *cas,
 
        ref->expect_old_sha1 = 1;
        if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
-               ref->expect_old_no_trackback = 1;
+               oidclr(&ref->old_oid_expect);
 }
 
 void apply_push_cas(struct push_cas_option *cas,
index c21fd3788c78f28d57e7d76d472dc411986b34d1..924881169d9f6c5b9b09e2434d964f62e0e28d09 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -89,7 +89,6 @@ struct ref {
                force:1,
                forced_update:1,
                expect_old_sha1:1,
-               expect_old_no_trackback:1,
                deletion:1,
                matched:1;
 
index 3a842ac2d1aa351e5182f0c67b74aee1bd67d021..90f2ac51a741f14cecab51177120cc7c3f063096 100644 (file)
@@ -266,7 +266,7 @@ static int generate_push_cert(struct strbuf *req_buf,
        struct strbuf cert = STRBUF_INIT;
        int update_seen = 0;
 
-       strbuf_addf(&cert, "certificate version 0.1\n");
+       strbuf_addstr(&cert, "certificate version 0.1\n");
        strbuf_addf(&cert, "pusher %s ", signing_key);
        datestamp(&cert);
        strbuf_addch(&cert, '\n');
index cdfac82b117e9ff1084f3fbe36eeb85f45ce056c..2e9c7d0f03734c20f1fb9d5c6214a7960d80b112 100644 (file)
@@ -112,7 +112,7 @@ static void remove_sequencer_state(void)
 {
        struct strbuf seq_dir = STRBUF_INIT;
 
-       strbuf_addf(&seq_dir, "%s", git_path(SEQ_DIR));
+       strbuf_addstr(&seq_dir, git_path(SEQ_DIR));
        remove_dir_recursively(&seq_dir, 0);
        strbuf_release(&seq_dir);
 }
@@ -293,6 +293,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        clean = merge_trees(&o,
                            head_tree,
                            next_tree, base_tree, &result);
+       strbuf_release(&o.obuf);
+       if (clean < 0)
+               return clean;
 
        if (active_cache_changed &&
            write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
@@ -559,6 +562,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
                res = do_recursive_merge(base, next, base_label, next_label,
                                         head, &msgbuf, opts);
+               if (res < 0)
+                       return res;
                write_message(&msgbuf, git_path_merge_msg());
        } else {
                struct commit_list *common = NULL;
index cb571ac6e8ed0657e39b346b41961caa8cc825be..02940f1920300c290e922174159fd7177844adda 100644 (file)
@@ -23,6 +23,7 @@
 #include "bulk-checkin.h"
 #include "streaming.h"
 #include "dir.h"
+#include "mru.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -59,14 +60,6 @@ static struct cached_object empty_tree = {
        0
 };
 
-/*
- * A pointer to the last packed_git in which an object was found.
- * When an object is sought, we look in this packfile first, because
- * objects that are looked up at similar times are often in the same
- * packfile as one another.
- */
-static struct packed_git *last_found_pack;
-
 static struct cached_object *find_cached_object(const unsigned char *sha1)
 {
        int i;
@@ -522,6 +515,9 @@ static size_t peak_pack_mapped;
 static size_t pack_mapped;
 struct packed_git *packed_git;
 
+static struct mru packed_git_mru_storage;
+struct mru *packed_git_mru = &packed_git_mru_storage;
+
 void pack_report(void)
 {
        fprintf(stderr,
@@ -795,7 +791,7 @@ void close_all_packs(void)
 
        for (p = packed_git; p; p = p->next)
                if (p->do_not_close)
-                       die("BUG! Want to close pack marked 'do-not-close'");
+                       die("BUG: want to close pack marked 'do-not-close'");
                else
                        close_pack(p);
 }
@@ -891,36 +887,6 @@ void close_pack_index(struct packed_git *p)
        }
 }
 
-/*
- * This is used by git-repack in case a newly created pack happens to
- * contain the same set of objects as an existing one.  In that case
- * the resulting file might be different even if its name would be the
- * same.  It is best to close any reference to the old pack before it is
- * replaced on disk.  Of course no index pointers or windows for given pack
- * must subsist at this point.  If ever objects from this pack are requested
- * again, the new version of the pack will be reinitialized through
- * reprepare_packed_git().
- */
-void free_pack_by_name(const char *pack_name)
-{
-       struct packed_git *p, **pp = &packed_git;
-
-       while (*pp) {
-               p = *pp;
-               if (strcmp(pack_name, p->pack_name) == 0) {
-                       clear_delta_base_cache();
-                       close_pack(p);
-                       free(p->bad_object_sha1);
-                       *pp = p->next;
-                       if (last_found_pack == p)
-                               last_found_pack = NULL;
-                       free(p);
-                       return;
-               }
-               pp = &p->next;
-       }
-}
-
 static unsigned int get_max_fd_limit(void)
 {
 #ifdef RLIMIT_NOFILE
@@ -1385,6 +1351,15 @@ static void rearrange_packed_git(void)
        free(ary);
 }
 
+static void prepare_packed_git_mru(void)
+{
+       struct packed_git *p;
+
+       mru_clear(packed_git_mru);
+       for (p = packed_git; p; p = p->next)
+               mru_append(packed_git_mru, p);
+}
+
 static int prepare_packed_git_run_once = 0;
 void prepare_packed_git(void)
 {
@@ -1400,6 +1375,7 @@ void prepare_packed_git(void)
                alt->name[-1] = '/';
        }
        rearrange_packed_git();
+       prepare_packed_git_mru();
        prepare_packed_git_run_once = 1;
 }
 
@@ -2330,7 +2306,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
        case OBJ_OFS_DELTA:
        case OBJ_REF_DELTA:
                if (data)
-                       die("BUG in unpack_entry: left loop at a valid delta");
+                       die("BUG: unpack_entry: left loop at a valid delta");
                break;
        case OBJ_COMMIT:
        case OBJ_TREE:
@@ -2604,21 +2580,15 @@ static int fill_pack_entry(const unsigned char *sha1,
  */
 static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 {
-       struct packed_git *p;
+       struct mru_entry *p;
 
        prepare_packed_git();
        if (!packed_git)
                return 0;
 
-       if (last_found_pack && fill_pack_entry(sha1, e, last_found_pack))
-               return 1;
-
-       for (p = packed_git; p; p = p->next) {
-               if (p == last_found_pack)
-                       continue; /* we already checked this one */
-
-               if (fill_pack_entry(sha1, e, p)) {
-                       last_found_pack = p;
+       for (p = packed_git_mru->head; p; p = p->next) {
+               if (fill_pack_entry(sha1, e, p->item)) {
+                       mru_mark(packed_git_mru, p);
                        return 1;
                }
        }
index 4d554caf8d751d7a7c8a3a4286a208e0959ab513..54e2db73349ef22d644552c6b122ae5ed6ba2ce1 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -389,7 +389,7 @@ static void paint_down(struct paint_info *info, const unsigned char *sha1,
        unsigned int i, nr;
        struct commit_list *head = NULL;
        int bitmap_nr = (info->nr_bits + 31) / 32;
-       size_t bitmap_size = st_mult(bitmap_nr, sizeof(uint32_t));
+       size_t bitmap_size = st_mult(sizeof(uint32_t), bitmap_nr);
        uint32_t *tmp = xmalloc(bitmap_size); /* to be freed before return */
        uint32_t *bitmap = paint_alloc(info);
        struct commit *c = lookup_commit_reference_gently(sha1, 1);
index faedb1a4899639567d8d6d6af75338c26be8f7ae..098085be69b97687b0febfe9e29c6dc0c64569a1 100644 (file)
@@ -59,6 +59,7 @@ static void free_one_config(struct submodule_entry *entry)
 {
        free((void *) entry->config->path);
        free((void *) entry->config->name);
+       free((void *) entry->config->branch);
        free((void *) entry->config->update_strategy.command);
        free(entry->config);
 }
@@ -199,6 +200,7 @@ static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache,
        submodule->update_strategy.command = NULL;
        submodule->fetch_recurse = RECURSE_SUBMODULES_NONE;
        submodule->ignore = NULL;
+       submodule->branch = NULL;
        submodule->recommend_shallow = -1;
 
        hashcpy(submodule->gitmodules_sha1, gitmodules_sha1);
@@ -358,9 +360,16 @@ static int parse_config(const char *var, const char *value, void *data)
                if (!me->overwrite && submodule->recommend_shallow != -1)
                        warn_multiple_config(me->commit_sha1, submodule->name,
                                             "shallow");
-               else {
+               else
                        submodule->recommend_shallow =
                                git_config_bool(var, value);
+       } else if (!strcmp(item.buf, "branch")) {
+               if (!me->overwrite && submodule->branch)
+                       warn_multiple_config(me->commit_sha1, submodule->name,
+                                            "branch");
+               else {
+                       free((void *)submodule->branch);
+                       submodule->branch = xstrdup(value);
                }
        }
 
@@ -442,7 +451,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
        parameter.commit_sha1 = commit_sha1;
        parameter.gitmodules_sha1 = sha1;
        parameter.overwrite = 0;
-       git_config_from_mem(parse_config, "submodule-blob", rev.buf,
+       git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
                        config, config_size, &parameter);
        strbuf_release(&rev);
        free(config);
index b1fdcc0c33326f556d23ce65ad21b01514fcb927..d05c542d2cdace181ea72055c0f71db6b1afda42 100644 (file)
@@ -15,6 +15,7 @@ struct submodule {
        const char *url;
        int fetch_recurse;
        const char *ignore;
+       const char *branch;
        struct submodule_update_strategy update_strategy;
        /* the sha1 blob id of the responsible .gitmodules file */
        unsigned char gitmodules_sha1[20];
index c71ea4f759bf15253841bda5dd95f0783d0f7ad6..d24d157379f30cafdeeb772e82bf5ee4c862272c 100644 (file)
@@ -26,7 +26,7 @@ static int parallel_next(struct child_process *cp,
                return 0;
 
        argv_array_pushv(&cp->args, d->argv);
-       strbuf_addf(err, "preloaded output of a child\n");
+       strbuf_addstr(err, "preloaded output of a child\n");
        number_callbacks++;
        return 1;
 }
@@ -36,7 +36,7 @@ static int no_job(struct child_process *cp,
                  void *cb,
                  void **task_cb)
 {
-       strbuf_addf(err, "no further jobs available\n");
+       strbuf_addstr(err, "no further jobs available\n");
        return 0;
 }
 
@@ -45,7 +45,7 @@ static int task_finished(int result,
                         void *pp_cb,
                         void *pp_task_cb)
 {
-       strbuf_addf(err, "asking for a quick stop\n");
+       strbuf_addstr(err, "asking for a quick stop\n");
        return 1;
 }
 
index fb8823224e9ed2452c32e847bf53dbc4da116f1b..688313ed5cc40e22b2412c57f98df06e4f3ac0ea 100644 (file)
@@ -65,81 +65,22 @@ svn_cmd () {
        svn "$orig_svncmd" --config-dir "$svnconf" "$@"
 }
 
-prepare_httpd () {
-       for d in \
-               "$SVN_HTTPD_PATH" \
-               /usr/sbin/apache2 \
-               /usr/sbin/httpd \
-       ; do
-               if test -f "$d"
-               then
-                       SVN_HTTPD_PATH="$d"
-                       break
-               fi
-       done
-       if test -z "$SVN_HTTPD_PATH"
-       then
-               echo >&2 '*** error: Apache not found'
-               return 1
-       fi
-       for d in \
-               "$SVN_HTTPD_MODULE_PATH" \
-               /usr/lib/apache2/modules \
-               /usr/libexec/apache2 \
-       ; do
-               if test -d "$d"
-               then
-                       SVN_HTTPD_MODULE_PATH="$d"
-                       break
-               fi
-       done
-       if test -z "$SVN_HTTPD_MODULE_PATH"
-       then
-               echo >&2 '*** error: Apache module dir not found'
-               return 1
-       fi
-       if test ! -f "$SVN_HTTPD_MODULE_PATH/mod_dav_svn.so"
-       then
-               echo >&2 '*** error: Apache module "mod_dav_svn" not found'
-               return 1
-       fi
-
-       repo_base_path="${1-svn}"
-       mkdir "$GIT_DIR"/logs
-
-       cat > "$GIT_DIR/httpd.conf" <<EOF
-ServerName "git svn test"
-ServerRoot "$GIT_DIR"
-DocumentRoot "$GIT_DIR"
-PidFile "$GIT_DIR/httpd.pid"
-LockFile logs/accept.lock
-Listen 127.0.0.1:$SVN_HTTPD_PORT
-LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
-LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
-<Location /$repo_base_path>
-       DAV svn
-       SVNPath "$rawsvnrepo"
-</Location>
-EOF
-}
-
-start_httpd () {
-       if test -z "$SVN_HTTPD_PORT"
-       then
-               echo >&2 'SVN_HTTPD_PORT is not defined!'
-               return
-       fi
-
-       prepare_httpd "$1" || return 1
-
-       "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
-       svnrepo="http://127.0.0.1:$SVN_HTTPD_PORT/$repo_base_path"
-}
-
-stop_httpd () {
-       test -z "$SVN_HTTPD_PORT" && return
-       test ! -f "$GIT_DIR/httpd.conf" && return
-       "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
+maybe_start_httpd () {
+       loc=${1-svn}
+
+       test_tristate GIT_SVN_TEST_HTTPD
+       case $GIT_SVN_TEST_HTTPD in
+       true)
+               . "$TEST_DIRECTORY"/lib-httpd.sh
+               LIB_HTTPD_SVN="$loc"
+               start_httpd
+               ;;
+       *)
+               stop_httpd () {
+                       : noop
+               }
+               ;;
+       esac
 }
 
 convert_to_rev_db () {
index ac2cbee250887759b67898e4a2ce9d9c2708b7bc..435a37465a702c35a5d53d8809f0ef700ae9ff83 100644 (file)
@@ -24,7 +24,7 @@
 #    LIB_HTTPD_MODULE_PATH       web server modules path
 #    LIB_HTTPD_PORT              listening port
 #    LIB_HTTPD_DAV               enable DAV
-#    LIB_HTTPD_SVN               enable SVN
+#    LIB_HTTPD_SVN               enable SVN at given location (e.g. "svn")
 #    LIB_HTTPD_SSL               enable SSL
 #
 # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
@@ -162,8 +162,10 @@ prepare_httpd() {
                if test -n "$LIB_HTTPD_SVN"
                then
                        HTTPD_PARA="$HTTPD_PARA -DSVN"
-                       rawsvnrepo="$HTTPD_ROOT_PATH/svnrepo"
-                       svnrepo="http://127.0.0.1:$LIB_HTTPD_PORT/svn"
+                       LIB_HTTPD_SVNPATH="$rawsvnrepo"
+                       svnrepo="http://127.0.0.1:$LIB_HTTPD_PORT/"
+                       svnrepo="$svnrepo$LIB_HTTPD_SVN"
+                       export LIB_HTTPD_SVN LIB_HTTPD_SVNPATH
                fi
        fi
 }
index 018a83a5a18431f120672b7cd8874bfb72e8d8fb..c3e631394f4a47f32e62e266431607861929f328 100644 (file)
@@ -208,8 +208,8 @@ RewriteRule ^/half-auth-complete/ - [E=AUTHREQUIRED:yes]
 <IfDefine SVN>
        LoadModule dav_svn_module modules/mod_dav_svn.so
 
-       <Location /svn>
+       <Location /${LIB_HTTPD_SVN}>
                DAV svn
-               SVNPath svnrepo
+               SVNPath "${LIB_HTTPD_SVNPATH}"
        </Location>
 </IfDefine>
diff --git a/t/perf/p5303-many-packs.sh b/t/perf/p5303-many-packs.sh
new file mode 100755 (executable)
index 0000000..3779851
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='performance with large numbers of packs'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+# A real many-pack situation would probably come from having a lot of pushes
+# over time. We don't know how big each push would be, but we can fake it by
+# just walking the first-parent chain and having every 5 commits be their own
+# "push". This isn't _entirely_ accurate, as real pushes would have some
+# duplicate objects due to thin-pack fixing, but it's a reasonable
+# approximation.
+#
+# And then all of the rest of the objects can go in a single packfile that
+# represents the state before any of those pushes (actually, we'll generate
+# that first because in such a setup it would be the oldest pack, and we sort
+# the packs by reverse mtime inside git).
+repack_into_n () {
+       rm -rf staging &&
+       mkdir staging &&
+
+       git rev-list --first-parent HEAD |
+       sed -n '1~5p' |
+       head -n "$1" |
+       perl -e 'print reverse <>' \
+       >pushes
+
+       # create base packfile
+       head -n 1 pushes |
+       git pack-objects --delta-base-offset --revs staging/pack
+
+       # and then incrementals between each pair of commits
+       last= &&
+       while read rev
+       do
+               if test -n "$last"; then
+                       {
+                               echo "$rev" &&
+                               echo "^$last"
+                       } |
+                       git pack-objects --delta-base-offset --revs \
+                               staging/pack || return 1
+               fi
+               last=$rev
+       done <pushes &&
+
+       # and install the whole thing
+       rm -f .git/objects/pack/* &&
+       mv staging/* .git/objects/pack/
+}
+
+# Pretend we just have a single branch and no reflogs, and that everything is
+# in objects/pack; that makes our fake pack-building via repack_into_n()
+# much simpler.
+test_expect_success 'simplify reachability' '
+       tip=$(git rev-parse --verify HEAD) &&
+       git for-each-ref --format="option no-deref%0adelete %(refname)" |
+       git update-ref --stdin &&
+       rm -rf .git/logs &&
+       git update-ref refs/heads/master $tip &&
+       git symbolic-ref HEAD refs/heads/master &&
+       git repack -ad
+'
+
+for nr_packs in 1 50 1000
+do
+       test_expect_success "create $nr_packs-pack scenario" '
+               repack_into_n $nr_packs
+       '
+
+       test_perf "rev-list ($nr_packs)" '
+               git rev-list --objects --all >/dev/null
+       '
+
+       # This simulates the interesting part of the repack, which is the
+       # actual pack generation, without smudging the on-disk setup
+       # between trials.
+       test_perf "repack ($nr_packs)" '
+               git pack-objects --keep-true-parents \
+                 --honor-pack-keep --non-empty --all \
+                 --reflog --indexed-objects --delta-base-offset \
+                 --stdout </dev/null >/dev/null
+       '
+done
+
+test_done
index 4c8cf58512513848d06f759d8e86860c5606dffa..c0c910867d75368832ce8b297e9dd82ee984a85a 100755 (executable)
@@ -46,7 +46,10 @@ check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 16:13:20 +0200'
 check_show short "$TIME" '2016-06-15'
 check_show default "$TIME" 'Wed Jun 15 16:13:20 2016 +0200'
 check_show raw "$TIME" '1466000000 +0200'
+check_show unix "$TIME" '1466000000'
 check_show iso-local "$TIME" '2016-06-15 14:13:20 +0000'
+check_show raw-local "$TIME" '1466000000 +0000'
+check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
index 4865304ebb8560d40fd993dc13a47a2ed4bf9e1b..2978cb9d640d1e826e8fb9b96e99bc69ee86f09d 100755 (executable)
@@ -7,6 +7,20 @@ test_description='Test of git add, including the -- option.'
 
 . ./test-lib.sh
 
+# Test the file mode "$1" of the file "$2" in the index.
+test_mode_in_index () {
+       case "$(git ls-files -s "$2")" in
+       "$1 "*" $2")
+               echo pass
+               ;;
+       *)
+               echo fail
+               git ls-files -s "$2"
+               return 1
+               ;;
+       esac
+}
+
 test_expect_success \
     'Test of git add' \
     'touch foo && git add foo'
@@ -25,18 +39,12 @@ test_expect_success \
         echo foo >xfoo1 &&
         chmod 755 xfoo1 &&
         git add xfoo1 &&
-        case "$(git ls-files --stage xfoo1)" in
-        100644" "*xfoo1) echo pass;;
-        *) echo fail; git ls-files --stage xfoo1; (exit 1);;
-        esac'
+        test_mode_in_index 100644 xfoo1'
 
 test_expect_success 'git add: filemode=0 should not get confused by symlink' '
        rm -f xfoo1 &&
        test_ln_s_add foo xfoo1 &&
-       case "$(git ls-files --stage xfoo1)" in
-       120000" "*xfoo1) echo pass;;
-       *) echo fail; git ls-files --stage xfoo1; (exit 1);;
-       esac
+       test_mode_in_index 120000 xfoo1
 '
 
 test_expect_success \
@@ -45,28 +53,19 @@ test_expect_success \
         echo foo >xfoo2 &&
         chmod 755 xfoo2 &&
         git update-index --add xfoo2 &&
-        case "$(git ls-files --stage xfoo2)" in
-        100644" "*xfoo2) echo pass;;
-        *) echo fail; git ls-files --stage xfoo2; (exit 1);;
-        esac'
+        test_mode_in_index 100644 xfoo2'
 
 test_expect_success 'git add: filemode=0 should not get confused by symlink' '
        rm -f xfoo2 &&
        test_ln_s_add foo xfoo2 &&
-       case "$(git ls-files --stage xfoo2)" in
-       120000" "*xfoo2) echo pass;;
-       *) echo fail; git ls-files --stage xfoo2; (exit 1);;
-       esac
+       test_mode_in_index 120000 xfoo2
 '
 
 test_expect_success \
        'git update-index --add: Test that executable bit is not used...' \
        'git config core.filemode 0 &&
         test_ln_s_add xfoo2 xfoo3 &&   # runs git update-index --add
-        case "$(git ls-files --stage xfoo3)" in
-        120000" "*xfoo3) echo pass;;
-        *) echo fail; git ls-files --stage xfoo3; (exit 1);;
-        esac'
+        test_mode_in_index 120000 xfoo3'
 
 test_expect_success '.gitignore test setup' '
        echo "*.ig" >.gitignore &&
@@ -332,34 +331,22 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file out
        test_i18ncmp expect.err actual.err
 '
 
-test_expect_success 'git add --chmod=+x stages a non-executable file with +x' '
+test_expect_success 'git add --chmod=[+-]x stages correctly' '
+       rm -f foo1 &&
        echo foo >foo1 &&
        git add --chmod=+x foo1 &&
-       case "$(git ls-files --stage foo1)" in
-       100755" "*foo1) echo pass;;
-       *) echo fail; git ls-files --stage foo1; (exit 1);;
-       esac
-'
-
-test_expect_success 'git add --chmod=-x stages an executable file with -x' '
-       echo foo >xfoo1 &&
-       chmod 755 xfoo1 &&
-       git add --chmod=-x xfoo1 &&
-       case "$(git ls-files --stage xfoo1)" in
-       100644" "*xfoo1) echo pass;;
-       *) echo fail; git ls-files --stage xfoo1; (exit 1);;
-       esac
+       test_mode_in_index 100755 foo1 &&
+       git add --chmod=-x foo1 &&
+       test_mode_in_index 100644 foo1
 '
 
 test_expect_success POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
        git config core.filemode 1 &&
        git config core.symlinks 1 &&
+       rm -f foo2 &&
        echo foo >foo2 &&
        git add --chmod=+x foo2 &&
-       case "$(git ls-files --stage foo2)" in
-       100755" "*foo2) echo pass;;
-       *) echo fail; git ls-files --stage foo2; (exit 1);;
-       esac
+       test_mode_in_index 100755 foo2
 '
 
 test_done
index 1206c48392c36db04ef422813e76b370eeef20b2..b0579dd45242f64d129d3c10e8ae1c3de6cc5458 100755 (executable)
@@ -229,6 +229,46 @@ check_patch () {
        grep -e "^Subject:" "$1"
 }
 
+test_expect_success 'format.from=false' '
+
+       git -c format.from=false format-patch --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       ! grep "^From: C O Mitter <committer@example.com>\$" patch
+'
+
+test_expect_success 'format.from=true' '
+
+       git -c format.from=true format-patch --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       grep "^From: C O Mitter <committer@example.com>\$" patch
+'
+
+test_expect_success 'format.from with address' '
+
+       git -c format.from="F R Om <from@example.com>" format-patch --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       grep "^From: F R Om <from@example.com>\$" patch
+'
+
+test_expect_success '--no-from overrides format.from' '
+
+       git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       ! grep "^From: F R Om <from@example.com>\$" patch
+'
+
+test_expect_success '--from overrides format.from' '
+
+       git -c format.from="F R Om <from@example.com>" format-patch --from --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       ! grep "^From: F R Om <from@example.com>\$" patch
+'
+
 test_expect_success '--no-to overrides config.to' '
 
        git config --replace-all format.to \
index d173acde0f2c44031003144fda9770f4b1e726b4..f8a313bcb98c6e2b98982295773bdc8ac7e13256 100755 (executable)
@@ -13,9 +13,13 @@ create_file() {
 }
 
 test_expect_success 'setup' '
-       create_file file1 "File1 contents" &&
-       create_file file2 "File2 contents" &&
-       create_file file3 "File3 contents" &&
+       # Ensure that file sizes are different, because on Windows
+       # lstat() does not discover inode numbers, and we need
+       # other properties to discover swapped files
+       # (mtime is not always different, either).
+       create_file file1 "some content" &&
+       create_file file2 "some other content" &&
+       create_file file3 "again something else" &&
        git add file1 file2 file3 &&
        git commit -m 1
 '
index d9f62425b052b4c136ce1f961d96b5063ce9a158..f5435fd250baf7415fb6a205f4497cb5c118de65 100755 (executable)
@@ -145,199 +145,199 @@ test_expect_success 'setup more commits' '
 
 test_expect_success 'left alignment formatting' '
        git log --pretty="tformat:%<(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-message two                            Z
-message one                            Z
-add bar                                Z
-$(commit_msg)                    Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       message two                            Z
+       message one                            Z
+       add bar                                Z
+       $(commit_msg)                    Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two                            Z
-message one                            Z
-add bar                                Z
-$(commit_msg)                    Z
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       message two                            Z
+       message one                            Z
+       add bar                                Z
+       $(commit_msg)                    Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting at the nth column' '
        git log --pretty="tformat:%h %<|(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1 message two                    Z
-$head2 message one                    Z
-$head3 add bar                        Z
-$head4 $(commit_msg)            Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1 message two                    Z
+       $head2 message one                    Z
+       $head3 add bar                        Z
+       $head4 $(commit_msg)            Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting at the nth column' '
        COLUMNS=50 git log --pretty="tformat:%h %<|(-10)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1 message two                    Z
-$head2 message one                    Z
-$head3 add bar                        Z
-$head4 $(commit_msg)            Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1 message two                    Z
+       $head2 message one                    Z
+       $head3 add bar                        Z
+       $head4 $(commit_msg)            Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting at the nth column. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %<|(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-$head1 message two                    Z
-$head2 message one                    Z
-$head3 add bar                        Z
-$head4 $(commit_msg)            Z
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       $head1 message two                    Z
+       $head2 message one                    Z
+       $head3 add bar                        Z
+       $head4 $(commit_msg)            Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with no padding' '
        git log --pretty="tformat:%<(1)%s" >actual &&
-       cat <<EOF >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with no padding. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(1)%s" >actual &&
-       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with trunc' '
        git log --pretty="tformat:%<(10,trunc)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-message ..
-message ..
-add bar  Z
-initial...
-EOF
+       qz_to_tab_space <<-\EOF >expected &&
+       message ..
+       message ..
+       add bar  Z
+       initial...
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with trunc. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message ..
-message ..
-add bar  Z
-initial...
-EOF
+       qz_to_tab_space <<-\EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       message ..
+       message ..
+       add bar  Z
+       initial...
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with ltrunc' '
        git log --pretty="tformat:%<(10,ltrunc)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-..sage two
-..sage one
-add bar  Z
-..${sample_utf8_part}lich
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       ..sage two
+       ..sage one
+       add bar  Z
+       ..${sample_utf8_part}lich
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with ltrunc. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,ltrunc)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-..sage two
-..sage one
-add bar  Z
-..${sample_utf8_part}lich
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       ..sage two
+       ..sage one
+       add bar  Z
+       ..${sample_utf8_part}lich
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with mtrunc' '
        git log --pretty="tformat:%<(10,mtrunc)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-mess.. two
-mess.. one
-add bar  Z
-init..lich
-EOF
+       qz_to_tab_space <<-\EOF >expected &&
+       mess.. two
+       mess.. one
+       add bar  Z
+       init..lich
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left alignment formatting with mtrunc. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,mtrunc)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-mess.. two
-mess.. one
-add bar  Z
-init..lich
-EOF
+       qz_to_tab_space <<-\EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       mess.. two
+       mess.. one
+       add bar  Z
+       init..lich
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting' '
        git log --pretty="tformat:%>(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-Z                            message two
-Z                            message one
-Z                                add bar
-Z                    $(commit_msg)
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       Z                            message two
+       Z                            message one
+       Z                                add bar
+       Z                    $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-Z                            message two
-Z                            message one
-Z                                add bar
-Z                    $(commit_msg)
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       Z                            message two
+       Z                            message one
+       Z                                add bar
+       Z                    $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting at the nth column' '
        git log --pretty="tformat:%h %>|(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1                      message two
-$head2                      message one
-$head3                          add bar
-$head4              $(commit_msg)
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1                      message two
+       $head2                      message one
+       $head3                          add bar
+       $head4              $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting at the nth column' '
        COLUMNS=50 git log --pretty="tformat:%h %>|(-10)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1                      message two
-$head2                      message one
-$head3                          add bar
-$head4              $(commit_msg)
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1                      message two
+       $head2                      message one
+       $head3                          add bar
+       $head4              $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting at the nth column. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %>|(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-$head1                      message two
-$head2                      message one
-$head3                          add bar
-$head4              $(commit_msg)
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       $head1                      message two
+       $head2                      message one
+       $head3                          add bar
+       $head4              $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
@@ -345,110 +345,110 @@ EOF
 # as in previous test.
 test_expect_success 'right alignment formatting at the nth column with --graph. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --graph --pretty="tformat:%h %>|(40)%s" >actual &&
-       iconv -f utf-8 -t $test_encoding >expected <<EOF&&
-* $head1                    message two
-* $head2                    message one
-* $head3                        add bar
-* $head4            $(commit_msg)
-EOF
+       iconv -f utf-8 -t $test_encoding >expected <<-EOF &&
+       * $head1                    message two
+       * $head2                    message one
+       * $head3                        add bar
+       * $head4            $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting with no padding' '
        git log --pretty="tformat:%>(1)%s" >actual &&
-       cat <<EOF >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting with no padding and with --graph' '
        git log --graph --pretty="tformat:%>(1)%s" >actual &&
-       cat <<EOF >expected &&
-* message two
-* message one
-* add bar
-* $(commit_msg)
-EOF
+       cat <<-EOF >expected &&
+       * message two
+       * message one
+       * add bar
+       * $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'right alignment formatting with no padding. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(1)%s" >actual &&
-       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'center alignment formatting' '
        git log --pretty="tformat:%><(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-Z             message two              Z
-Z             message one              Z
-Z               add bar                Z
-Z         $(commit_msg)          Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       Z             message two              Z
+       Z             message one              Z
+       Z               add bar                Z
+       Z         $(commit_msg)          Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'center alignment formatting. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-Z             message two              Z
-Z             message one              Z
-Z               add bar                Z
-Z         $(commit_msg)          Z
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       Z             message two              Z
+       Z             message one              Z
+       Z               add bar                Z
+       Z         $(commit_msg)          Z
+       EOF
        test_cmp expected actual
 '
 test_expect_success 'center alignment formatting at the nth column' '
        git log --pretty="tformat:%h %><|(40)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1           message two          Z
-$head2           message one          Z
-$head3             add bar            Z
-$head4       $(commit_msg)      Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1           message two          Z
+       $head2           message one          Z
+       $head3             add bar            Z
+       $head4       $(commit_msg)      Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'center alignment formatting at the nth column' '
        COLUMNS=70 git log --pretty="tformat:%h %><|(-30)%s" >actual &&
-       qz_to_tab_space <<EOF >expected &&
-$head1           message two          Z
-$head2           message one          Z
-$head3             add bar            Z
-$head4       $(commit_msg)      Z
-EOF
+       qz_to_tab_space <<-EOF >expected &&
+       $head1           message two          Z
+       $head2           message one          Z
+       $head3             add bar            Z
+       $head4       $(commit_msg)      Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'center alignment formatting at the nth column. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %><|(40)%s" >actual &&
-       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-$head1           message two          Z
-$head2           message one          Z
-$head3             add bar            Z
-$head4       $(commit_msg)      Z
-EOF
+       qz_to_tab_space <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       $head1           message two          Z
+       $head2           message one          Z
+       $head3             add bar            Z
+       $head4       $(commit_msg)      Z
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'center alignment formatting with no padding' '
        git log --pretty="tformat:%><(1)%s" >actual &&
-       cat <<EOF >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
@@ -457,34 +457,34 @@ EOF
 old_head1=$(git rev-parse --verify HEAD~0)
 test_expect_success 'center alignment formatting with no padding. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(1)%s" >actual &&
-       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-message two
-message one
-add bar
-$(commit_msg)
-EOF
+       cat <<-EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       message two
+       message one
+       add bar
+       $(commit_msg)
+       EOF
        test_cmp expected actual
 '
 
 test_expect_success 'left/right alignment formatting with stealing' '
        git commit --amend -m short --author "long long long <long@me.com>" &&
        git log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
-       cat <<EOF >expected &&
-short long  long long
-message ..   A U Thor
-add bar      A U Thor
-initial...   A U Thor
-EOF
+       cat <<-\EOF >expected &&
+       short long  long long
+       message ..   A U Thor
+       add bar      A U Thor
+       initial...   A U Thor
+       EOF
        test_cmp expected actual
 '
 test_expect_success 'left/right alignment formatting with stealing. i18n.logOutputEncoding' '
        git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
-       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
-short long  long long
-message ..   A U Thor
-add bar      A U Thor
-initial...   A U Thor
-EOF
+       cat <<-\EOF | iconv -f utf-8 -t $test_encoding >expected &&
+       short long  long long
+       message ..   A U Thor
+       add bar      A U Thor
+       initial...   A U Thor
+       EOF
        test_cmp expected actual
 '
 
@@ -504,8 +504,10 @@ test_expect_success 'ISO and ISO-strict date formats display the same values' '
 '
 
 # get new digests (with no abbreviations)
-head1=$(git rev-parse --verify HEAD~0) &&
-head2=$(git rev-parse --verify HEAD~1) &&
+test_expect_success 'set up log decoration tests' '
+       head1=$(git rev-parse --verify HEAD~0) &&
+       head2=$(git rev-parse --verify HEAD~1)
+'
 
 test_expect_success 'log decoration properly follows tag chain' '
        git tag -a tag1 -m tag1 &&
@@ -513,22 +515,22 @@ test_expect_success 'log decoration properly follows tag chain' '
        git tag -d tag1 &&
        git commit --amend -m shorter &&
        git log --no-walk --tags --pretty="%H %d" --decorate=full >actual &&
-       cat <<EOF >expected &&
-$head1  (tag: refs/tags/tag2)
-$head2  (tag: refs/tags/message-one)
-$old_head1  (tag: refs/tags/message-two)
-EOF
+       cat <<-EOF >expected &&
+       $head1  (tag: refs/tags/tag2)
+       $head2  (tag: refs/tags/message-one)
+       $old_head1  (tag: refs/tags/message-two)
+       EOF
        sort actual >actual1 &&
        test_cmp expected actual1
 '
 
 test_expect_success 'clean log decoration' '
        git log --no-walk --tags --pretty="%H %D" --decorate=full >actual &&
-       cat >expected <<EOF &&
-$head1 tag: refs/tags/tag2
-$head2 tag: refs/tags/message-one
-$old_head1 tag: refs/tags/message-two
-EOF
+       cat >expected <<-EOF &&
+       $head1 tag: refs/tags/tag2
+       $head2 tag: refs/tags/message-one
+       $old_head1 tag: refs/tags/message-two
+       EOF
        sort actual >actual1 &&
        test_cmp expected actual1
 '
index f8008b6a3d2a5fc200665998288d121d1c35dd25..b972296f0634144defe5a7a083c5607893a7ca10 100755 (executable)
@@ -44,7 +44,7 @@ test_expect_success setup '
 '
 
 cat >expected <<EOF
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}${c_commit} ->\
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD ->\
  ${c_reset}${c_branch}master${c_reset}${c_commit},\
  ${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit},\
  ${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
index 6bd4853079ea3215edaf2c409d8ae0548f5efe3a..668c54be41eb4fda17e9b43000081c7b374990c2 100755 (executable)
@@ -688,7 +688,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '
        )
 '
 
-test_expect_success 'fetch aligned output' '
+test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '
        git clone . full-output &&
        test_commit looooooooooooong-tag &&
        (
@@ -703,7 +703,7 @@ test_expect_success 'fetch aligned output' '
        test_cmp expect actual
 '
 
-test_expect_success 'fetch compact output' '
+test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '
        git clone . compact &&
        test_commit extraaa &&
        (
index 37ebbcfbbf0fd7815f598cf54d9c2c2825341f41..6ad37b5f6616d1423436dfa56a41da73b88f8bcf 100755 (executable)
@@ -255,6 +255,38 @@ test_expect_success '--rebase' '
        test new = "$(git show HEAD:file2)"
 '
 
+test_expect_success '--rebase with conflicts shows advice' '
+       test_when_finished "git rebase --abort; git checkout -f to-rebase" &&
+       git checkout -b seq &&
+       test_seq 5 >seq.txt &&
+       git add seq.txt &&
+       test_tick &&
+       git commit -m "Add seq.txt" &&
+       echo 6 >>seq.txt &&
+       test_tick &&
+       git commit -m "Append to seq.txt" seq.txt &&
+       git checkout -b with-conflicts HEAD^ &&
+       echo conflicting >>seq.txt &&
+       test_tick &&
+       git commit -m "Create conflict" seq.txt &&
+       test_must_fail git pull --rebase . seq 2>err >out &&
+       grep "When you have resolved this problem" out
+'
+
+test_expect_success 'failed --rebase shows advice' '
+       test_when_finished "git rebase --abort; git checkout -f to-rebase" &&
+       git checkout -b diverging &&
+       test_commit attributes .gitattributes "* text=auto" attrs &&
+       sha1="$(printf "1\\r\\n" | git hash-object -w --stdin)" &&
+       git update-index --cacheinfo 0644 $sha1 file &&
+       git commit -m v1-with-cr &&
+       # force checkout because `git reset --hard` will not leave clean `file`
+       git checkout -f -b fails-to-rebase HEAD^ &&
+       test_commit v2-without-cr file "2" file2-lf &&
+       test_must_fail git pull --rebase . diverging 2>err >out &&
+       grep "When you have resolved this problem" out
+'
+
 test_expect_success '--rebase fails with multiple branches' '
        git reset --hard before-rebase &&
        test_must_fail git pull --rebase . copy master 2>err &&
index c7320121ecfa74dce8389d3790364501c8bb12b8..a2c9e7439f362d8f247c369ba515a55b26dee8ec 100755 (executable)
@@ -191,4 +191,42 @@ test_expect_success 'cover everything with default force-with-lease (allowed)' '
        test_cmp expect actual
 '
 
+test_expect_success 'new branch covered by force-with-lease' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               git branch branch master &&
+               git push --force-with-lease=branch origin branch
+       ) &&
+       git ls-remote dst refs/heads/branch >expect &&
+       git ls-remote src refs/heads/branch >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'new branch covered by force-with-lease (explicit)' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               git branch branch master &&
+               git push --force-with-lease=branch: origin branch
+       ) &&
+       git ls-remote dst refs/heads/branch >expect &&
+       git ls-remote src refs/heads/branch >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'new branch already exists' '
+       setup_srcdst_basic &&
+       (
+               cd src &&
+               git checkout -b branch master &&
+               test_commit F
+       ) &&
+       (
+               cd dst &&
+               git branch branch master &&
+               test_must_fail git push --force-with-lease=branch: origin branch
+       )
+'
+
 test_done
index e4fc5c826c2c02d540c64ac88374f8ad85869023..c8dc665f2fdd0f0452779e6f8576395f8b3bdf11 100755 (executable)
@@ -49,6 +49,19 @@ test_expect_success TTY 'LESS and LV envvars are set for pagination' '
        grep ^LV= pager-env.out
 '
 
+test_expect_success !MINGW,TTY 'LESS and LV envvars set by git-sh-setup' '
+       (
+               sane_unset LESS LV &&
+               PAGER="env >pager-env.out; wc" &&
+               export PAGER &&
+               PATH="$(git --exec-path):$PATH" &&
+               export PATH &&
+               test_terminal sh -c ". git-sh-setup && git_pager"
+       ) &&
+       grep ^LESS= pager-env.out &&
+       grep ^LV= pager-env.out
+'
+
 test_expect_success TTY 'some commands do not use a pager' '
        rm -f paginated.out &&
        test_terminal git rev-list HEAD &&
index c23a21cb74d56426a9948c0a37db512b093eaa57..0667bd9dd3e8bc650bc4e47ac9fcde5d63ef0d11 100755 (executable)
@@ -4,6 +4,20 @@ test_description='test untracked cache'
 
 . ./test-lib.sh
 
+# On some filesystems (e.g. FreeBSD's ext2 and ufs) directory mtime
+# is updated lazily after contents in the directory changes, which
+# forces the untracked cache code to take the slow path.  A test
+# that wants to make sure that the fast path works correctly should
+# call this helper to make mtime of the containing directory in sync
+# with the reality before checking the fast path behaviour.
+#
+# See <20160803174522.5571-1-pclouds@gmail.com> if you want to know
+# more.
+
+sync_mtime () {
+       find . -type d -ls >/dev/null
+}
+
 avoid_racy() {
        sleep 1
 }
@@ -416,7 +430,8 @@ test_expect_success 'create/modify files, some of which are gitignored' '
        echo four >done/four && # four is gitignored at a higher level
        echo five >done/five && # five is not gitignored
        echo test >base && #we need to ensure that the root dir is touched
-       rm base
+       rm base &&
+       sync_mtime
 '
 
 test_expect_success 'test sparse status with untracked cache' '
index 88e9750abb2f2c0346385439e305dd07ec795a41..64f322c4cc87493ebc060ae5837495563fe61958 100755 (executable)
@@ -209,9 +209,42 @@ test_expect_success 'submodule update --remote should fetch upstream changes' '
        )
 '
 
+test_expect_success 'submodule update --remote should fetch upstream changes with .' '
+       (
+               cd super &&
+               git config -f .gitmodules submodule."submodule".branch "." &&
+               git add .gitmodules &&
+               git commit -m "submodules: update from the respective superproject branch"
+       ) &&
+       (
+               cd submodule &&
+               echo line4a >> file &&
+               git add file &&
+               test_tick &&
+               git commit -m "upstream line4a" &&
+               git checkout -b test-branch &&
+               test_commit on-test-branch
+       ) &&
+       (
+               cd super &&
+               git submodule update --remote --force submodule &&
+               git -C submodule log -1 --oneline >actual
+               git -C ../submodule log -1 --oneline master >expect
+               test_cmp expect actual &&
+               git checkout -b test-branch &&
+               git submodule update --remote --force submodule &&
+               git -C submodule log -1 --oneline >actual
+               git -C ../submodule log -1 --oneline test-branch >expect
+               test_cmp expect actual &&
+               git checkout master &&
+               git branch -d test-branch &&
+               git reset --hard HEAD^
+       )
+'
+
 test_expect_success 'local config should override .gitmodules branch' '
        (cd submodule &&
-        git checkout -b test-branch &&
+        git checkout test-branch &&
         echo line5 >> file &&
         git add file &&
         test_tick &&
@@ -841,16 +874,35 @@ test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd'
 '
 
 test_expect_success 'submodule update clone shallow submodule' '
+       test_when_finished "rm -rf super3" &&
+       first=$(git -C cloned submodule status submodule |cut -c2-41) &&
+       second=$(git -C submodule rev-parse HEAD) &&
+       commit_count=$(git -C submodule rev-list --count $first^..$second) &&
        git clone cloned super3 &&
        pwd=$(pwd) &&
-       (cd super3 &&
-        sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
-        mv -f .gitmodules.tmp .gitmodules &&
-        git submodule update --init --depth=3
-        (cd submodule &&
-         test 1 = $(git log --oneline | wc -l)
-        )
-)
+       (
+               cd super3 &&
+               sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
+               mv -f .gitmodules.tmp .gitmodules &&
+               git submodule update --init --depth=$commit_count &&
+               test 1 = $(git -C submodule log --oneline | wc -l)
+       )
+'
+
+test_expect_success 'submodule update clone shallow submodule outside of depth' '
+       test_when_finished "rm -rf super3" &&
+       git clone cloned super3 &&
+       pwd=$(pwd) &&
+       (
+               cd super3 &&
+               sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
+               mv -f .gitmodules.tmp .gitmodules &&
+               test_must_fail git submodule update --init --depth=1 2>actual &&
+               test_i18ngrep "Direct fetching of that commit failed." actual &&
+               git -C ../submodule config uploadpack.allowReachableSHA1InWant true &&
+               git submodule update --init --depth=1 >actual &&
+               test 1 = $(git -C submodule log --oneline | wc -l)
+       )
 '
 
 test_expect_success 'submodule update --recursive drops module name before recursing' '
index 42a2929835b5c77e5b3031ce56ad1243afb56971..2974900578d3f21b57d5773973344a1743e8e6e0 100755 (executable)
@@ -412,6 +412,20 @@ run_dir_diff_test 'difftool --dir-diff from subdirectory' '
        )
 '
 
+run_dir_diff_test 'difftool --dir-diff from subdirectory with GIT_DIR set' '
+       (
+               GIT_DIR=$(pwd)/.git &&
+               export GIT_DIR &&
+               GIT_WORK_TREE=$(pwd) &&
+               export GIT_WORK_TREE &&
+               cd sub &&
+               git difftool --dir-diff $symlinks --extcmd ls \
+                       branch -- sub >output &&
+               grep sub output &&
+               ! grep file output
+       )
+'
+
 run_dir_diff_test 'difftool --dir-diff when worktree file is missing' '
        test_when_finished git reset --hard &&
        rm file2 &&
index d29f60108a4235feae1068a6ea1a89cc7bee5b4b..92a3aa8063f810bd2b8df68f3f0f30c2faf3bdc6 100755 (executable)
@@ -8,8 +8,6 @@ GIT_SVN_LC_ALL=${LC_ALL:-$LANG}
 
 . ./lib-git-svn.sh
 
-say 'define NO_SVN_TESTS to skip git svn tests'
-
 case "$GIT_SVN_LC_ALL" in
 *.UTF-8)
        test_set_prereq UTF8
index a87d3d3fc1e2d6dca0fc6280e33637d2589d3235..64bb495834698c8438c8b8dde488873072abc1fd 100755 (executable)
@@ -8,9 +8,10 @@ test_description='git svn dcommit can commit renames of files with ugly names'
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
-       svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9115/funky-names.dump &&
-       start_httpd gtk+
-       '
+       svnadmin load -q "$rawsvnrepo" <"$TEST_DIRECTORY"/t9115/funky-names.dump
+'
+
+maybe_start_httpd gtk+
 
 test_expect_success 'init and fetch repository' '
        git svn init "$svnrepo" &&
index ecb1fed147d206c7d1a3ac43f9d82110e9514e66..41a026637fa03174765d4e3facc3815bbf538e29 100755 (executable)
@@ -32,7 +32,7 @@ test_expect_success 'setup svnrepo' '
                        "$svnrepo/pr ject/branches/trailing_dotlock.lock" &&
        svn_cmd cp -m "reflog" "$svnrepo/pr ject/trunk" \
                        "$svnrepo/pr ject/branches/not-a@{0}reflog@" &&
-       start_httpd
+       maybe_start_httpd
        '
 
 # SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
index 59465b147eb9cdc7da1fb891ff695cbc4442209a..b28a1741e3f52296dcab35ed6d51fa01c2628652 100755 (executable)
@@ -15,7 +15,7 @@ test_expect_success 'setup svnrepo' '
        svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \
          "$svnrepo/pr ject/tags/v1" &&
        rm -rf project &&
-       start_httpd
+       maybe_start_httpd
 '
 
 test_expect_success 'test clone with percent escapes' '
index e21ee5f663ce8333625c5d9d483c42dde3394675..9ee23be64003ab22277c80a6c4b364c8cc36f902 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success 'setup test repository' '
                svn_cmd add foo &&
                svn_cmd commit -m "add foo"
        ) &&
-       start_httpd
+       maybe_start_httpd
 '
 
 test_expect_success 'clone trunk with "-r HEAD"' '
index 13f78f2682602014711714a09a8f675c3eed1ff0..a875b4510270aa8411ab5ee546223be49a1fd65b 100755 (executable)
@@ -7,8 +7,6 @@ test_description='git svn mergeinfo propagation'
 
 . ./lib-git-svn.sh
 
-say 'define NO_SVN_TESTS to skip git svn tests'
-
 test_expect_success 'initialize source svn repo' '
        svn_cmd mkdir -m x "$svnrepo"/trunk &&
        svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
index b4a443460476bdeb783963e5a3b488b7ef6f4ad2..0ede3cfedb2a7b5ac66238a61615395001885a94 100755 (executable)
@@ -11,7 +11,6 @@ local Git repository with placeholder files.'
 
 . ./lib-git-svn.sh
 
-say 'define NO_SVN_TESTS to skip git svn tests'
 GIT_REPO=git-svn-repo
 
 test_expect_success 'initialize source svn repo containing empty dirs' '
index 8e48a5c9909ec7e8ba44e5b0678455b4dc8d2d02..c6ea9ac64d75292021919ad54462a3e4b782b080 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -562,7 +562,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
                        warning(_("unknown value '%s' for key '%s'"), value, conf_key);
                break;
        default:
-               die("internal bug in trailer.c");
+               die("BUG: trailer.c: unhandled type %d", type);
        }
        return 0;
 }
index 4ba48b05960203f9c5ed3360e99382bc945ae7e4..c5772a14eeec3d5651796c8051443dd76583c05d 100644 (file)
@@ -567,7 +567,7 @@ void transport_take_over(struct transport *transport,
        struct git_transport_data *data;
 
        if (!transport->smart_options)
-               die("Bug detected: Taking over transport requires non-NULL "
+               die("BUG: taking over transport requires non-NULL "
                    "smart_options field.");
 
        data = xcalloc(1, sizeof(*data));
index 3175ec6ddb6d6e40f8e2cfcbbb0b4acf5f0b5e98..6225a2d89f2c38bea6ed64295a28e7e07d19522c 100644 (file)
@@ -263,7 +263,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)
        case 7:
                return _("both modified:");
        default:
-               die("bug: unhandled unmerged status %x", stagemask);
+               die("BUG: unhandled unmerged status %x", stagemask);
        }
 }
 
@@ -388,7 +388,7 @@ static void wt_status_print_change_data(struct wt_status *s,
        status_printf(s, color(WT_STATUS_HEADER, s), "\t");
        what = wt_status_diff_status_string(status);
        if (!what)
-               die("bug: unhandled diff status %c", status);
+               die("BUG: unhandled diff status %c", status);
        len = label_width - utf8_strwidth(what);
        assert(len >= 0);
        if (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)