Merge branch 'sk/mingw-uni-fix-more'
authorJunio C Hamano <gitster@pobox.com>
Wed, 30 Jul 2014 21:21:09 +0000 (14:21 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 30 Jul 2014 21:21:09 +0000 (14:21 -0700)
Most of these are battle-tested in msysgit and are needed to
complete what has been merged to 'master' already.

* sk/mingw-uni-fix-more:
Win32: enable color output in Windows cmd.exe
Win32: patch Windows environment on startup
Win32: keep the environment sorted
Win32: use low-level memory allocation during initialization
Win32: reduce environment array reallocations
Win32: don't copy the environment twice when spawning child processes
Win32: factor out environment block creation
Win32: unify environment function names
Win32: unify environment case-sensitivity
Win32: fix environment memory leaks
Win32: Unicode environment (incoming)
Win32: Unicode environment (outgoing)
Revert "Windows: teach getenv to do a case-sensitive search"
tests: do not pass iso8859-1 encoded parameter

173 files changed:
.gitignore
.mailmap
Documentation/CodingGuidelines
Documentation/RelNotes/2.0.2.txt
Documentation/RelNotes/2.0.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.1.0.txt
Documentation/config.txt
Documentation/git-replace.txt
Documentation/git-rev-parse.txt
Documentation/git-tag.txt
Documentation/git-update-index.txt
Documentation/git.txt
Documentation/gitrepository-layout.txt
Documentation/technical/api-hashmap.txt
Documentation/technical/api-string-list.txt
Documentation/technical/api-trace.txt [new file with mode: 0644]
Documentation/technical/index-format.txt
GIT-VERSION-GEN
INSTALL
Makefile
alloc.c
blob.c
builtin/add.c
builtin/annotate.c
builtin/apply.c
builtin/blame.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/describe.c
builtin/diff-tree.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/index-pack.c
builtin/log.c
builtin/merge.c
builtin/mv.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/remote.c
builtin/repack.c
builtin/replace.c
builtin/reset.c
builtin/rev-parse.c
builtin/rm.c
builtin/show-branch.c
builtin/tag.c
builtin/update-index.c
builtin/update-ref.c
builtin/verify-pack.c
bundle.c
cache-tree.c
cache-tree.h
cache.h
commit.c
commit.h
compat/mingw.c
compat/mingw.h
config.c
config.mak.uname
connect.c
connected.c
contrib/convert-grafts-to-replace-refs.sh [new file with mode: 0755]
contrib/subtree/Makefile
daemon.c
decorate.c
diff.c
diffcore-rename.c
dir.c
dir.h
entry.c
ewah/ewah_io.c
ewah/ewok.h
fast-import.c
fsck.c
git-compat-util.h
git-filter-branch.sh
git-rebase--am.sh
git-rebase--interactive.sh
git-rebase.sh
git.c
hashmap.c
hashmap.h
help.c
http-backend.c
http-push.c
khash.h
line-log.c
lockfile.c
log-tree.c
merge-recursive.c
merge.c
name-hash.c
object.c
object.h
pack-objects.c
path.c
pathspec.c
pkt-line.c
preload-index.c
pretty.c
prio-queue.c
prio-queue.h
progress.c
read-cache.c
refs.c
refs.h
remote-curl.c
remote-testsvn.c
rerere.c
resolve-undo.c
run-command.c
sequencer.c
sh-i18n--envsubst.c
sha1_file.c
sha1_name.c
shallow.c
split-index.c [new file with mode: 0644]
split-index.h [new file with mode: 0644]
strbuf.c
strbuf.h
string-list.c
string-list.h
submodule.c
t/Makefile
t/lib-submodule-update.sh [new file with mode: 0755]
t/t0011-hashmap.sh
t/t0025-crlf-auto.sh
t/t0027-auto-crlf.sh [new file with mode: 0755]
t/t1013-read-tree-submodule.sh [new file with mode: 0755]
t/t1402-check-ref-format.sh
t/t1700-split-index.sh [new file with mode: 0755]
t/t2013-checkout-submodule.sh
t/t2104-update-index-skip-worktree.sh
t/t3400-rebase.sh
t/t3426-rebase-submodule.sh [new file with mode: 0755]
t/t3512-cherry-pick-submodule.sh [new file with mode: 0755]
t/t3513-revert-submodule.sh [new file with mode: 0755]
t/t3906-stash-submodule.sh [new file with mode: 0755]
t/t4013-diff-various.sh
t/t4137-apply-submodule.sh [new file with mode: 0755]
t/t4202-log.sh
t/t4255-am-submodule.sh [new file with mode: 0755]
t/t5539-fetch-http-shallow.sh
t/t5572-pull-submodule.sh [new file with mode: 0755]
t/t6023-merge-file.sh
t/t6041-bisect-submodule.sh [new file with mode: 0755]
t/t6050-replace.sh
t/t7003-filter-branch.sh
t/t7004-tag.sh
t/t7112-reset-submodule.sh [new file with mode: 0755]
t/t7613-merge-submodule.sh [new file with mode: 0755]
t/t7702-repack-cyclic-alternate.sh [new file with mode: 0755]
t/t9814-git-p4-rename.sh
t/test-lib-functions.sh
t/test-lib.sh
t/valgrind/default.supp
tag.c
test-dump-cache-tree.c
test-dump-split-index.c [new file with mode: 0644]
test-hashmap.c
test-scrap-cache-tree.c
trace.c
trace.h [new file with mode: 0644]
transport.c
tree.c
unix-socket.c
unpack-trees.c
url.c
wt-status.c
xdiff/xmerge.c
index 42294e59a1fcf5d744199b6a0feafd4ca6e3eda1..81e12c0621d42b495ab540391e1cd8ece18587b7 100644 (file)
 /git-upload-archive
 /git-upload-pack
 /git-var
+/git-verify-commit
 /git-verify-pack
 /git-verify-tag
 /git-web--browse
 /test-date
 /test-delta
 /test-dump-cache-tree
+/test-dump-split-index
 /test-scrap-cache-tree
 /test-genrandom
 /test-hashmap
index 11057cbcdf4c9f814189bdbf0a17980825da194c..8aefb5a45218b2d685feba7db196f03bb21a8f9d 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -85,6 +85,7 @@ Jeff King <peff@peff.net> <peff@github.com>
 Jeff Muizelaar <jmuizelaar@mozilla.com> <jeff@infidigm.net>
 Jens Axboe <axboe@kernel.dk> <axboe@suse.de>
 Jens Axboe <axboe@kernel.dk> <jens.axboe@oracle.com>
+Jens Lindström <jl@opera.com> Jens Lindstrom <jl@opera.com>
 Jim Meyering <jim@meyering.net> <meyering@redhat.com>
 Joachim Berdal Haga <cjhaga@fys.uio.no>
 Johannes Schindelin <Johannes.Schindelin@gmx.de> <johannes.schindelin@gmx.de>
@@ -113,6 +114,7 @@ Karsten Blees <blees@dcon.de> <karsten.blees@dcon.de>
 Karsten Blees <blees@dcon.de> <karsten.blees@gmail.com>
 Kay Sievers <kay.sievers@vrfy.org> <kay.sievers@suse.de>
 Kay Sievers <kay.sievers@vrfy.org> <kay@mam.(none)>
+Kazuki Saitoh <ksaitoh560@gmail.com> kazuki saitoh <ksaitoh560@gmail.com>
 Keith Cascio <keith@CS.UCLA.EDU> <keith@cs.ucla.edu>
 Kent Engstrom <kent@lysator.liu.se>
 Kevin Leung <kevinlsk@gmail.com>
@@ -202,6 +204,7 @@ Seth Falcon <seth@userprimary.net> <sfalcon@fhcrc.org>
 Shawn O. Pearce <spearce@spearce.org>
 Simon Hausmann <hausmann@kde.org> <simon@lst.de>
 Simon Hausmann <hausmann@kde.org> <shausman@trolltech.com>
+Stefan Beller <stefanbeller@gmail.com> <stefanbeller@googlemail.com>
 Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@atlas-elektronik.com>
 Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@googlemail.com>
 Stefan Sperling <stsp@elego.de> <stsp@stsp.name>
@@ -229,6 +232,7 @@ Tommi Virtanen <tv@debian.org> <tv@inoi.fi>
 Tommy Thorn <tommy-git@thorn.ws> <tt1729@yahoo.com>
 Tony Luck <tony.luck@intel.com>
 Tor Arne Vestbø <torarnv@gmail.com> <tavestbo@trolltech.com>
+Trần Ngọc Quân <vnwildman@gmail.com> Tran Ngoc Quan <vnwildman@gmail.com>
 Trent Piepho <tpiepho@gmail.com> <tpiepho@freescale.com>
 Trent Piepho <tpiepho@gmail.com> <xyzzy@speakeasy.org>
 Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <Uwe.Kleine-Koenig@digi.com>
index 4d90c77c7bd643b11df8d3094efe142493df53b9..894546dd75416fcf09542096a67b2f22a7d0de7a 100644 (file)
@@ -404,6 +404,15 @@ For Python scripts:
    documentation for version 2.6 does not mention this prefix, it has
    been supported since version 2.6.0.
 
+Error Messages
+
+ - Do not end error messages with a full stop.
+
+ - Do not capitalize ("unable to open %s", not "Unable to open %s")
+
+ - Say what the error is first ("cannot open %s", not "%s: cannot open")
+
+
 Writing Documentation:
 
  Most (if not all) of the documentation pages are written in the
index c48c0aab27053403693a796e8690e39e16dcb4ab..8e8321b2ef3bdbef2c16de51456718c95fe85d08 100644 (file)
@@ -11,3 +11,22 @@ Git v2.0.2 Release Notes
  * Recent updates to "git repack" started to duplicate objects that
    are in packfiles marked with .keep flag into the new packfile by
    mistake.
+
+ * "git clone -b brefs/tags/bar" would have mistakenly thought we were
+   following a single tag, even though it was a name of the branch,
+   because it incorrectly used strstr().
+
+ * "%G" (nothing after G) is an invalid pretty format specifier, but
+   the parser did not notice it as garbage.
+
+ * Code to avoid adding the same alternate object store twice was
+   subtly broken for a long time, but nobody seems to have noticed.
+
+ * A handful of code paths had to read the commit object more than
+   once when showing header fields that are usually not parsed.  The
+   internal data structure to keep track of the contents of the commit
+   object has been updated to reduce the need for this double-reading,
+   and to allow the caller find the length of the object.
+
+ * During "git rebase --merge", a conflicted patch could not be
+   skipped with "--skip" if the next one also conflicted.
diff --git a/Documentation/RelNotes/2.0.3.txt b/Documentation/RelNotes/2.0.3.txt
new file mode 100644 (file)
index 0000000..4047b46
--- /dev/null
@@ -0,0 +1,17 @@
+Git v2.0.3 Release Notes
+========================
+
+ * An ancient rewrite passed a wrong pointer to a curl library
+   function in a rarely used code path.
+
+ * "filter-branch" left an empty single-parent commit that results when
+   all parents of a merge commit gets mapped to the same commit, even
+   under "--prune-empty".
+
+ * "log --show-signature" incorrectly decided the color to paint a
+   mergetag that was and was not correctly validated.
+
+ * "log --show-signature" did not pay attention to "--graph" option.
+
+Also a lot of fixes to the tests and some updates to the docs are
+included.
index 3b65461c0b5a4384fbe28dfbd0e3a30e9a9a533a..be598ad7a2dee782004a854936e5e7aa2e4bac73 100644 (file)
@@ -49,6 +49,12 @@ UI, Workflows & Features
    enabled, allowing modern platforms to take advantage of the
    multiple cores they have.
 
+ * "git clone" applies the "if cloning from a local disk, physically
+   copy repository using hardlinks, unless otherwise told not to with
+   --no-local" optimization when url.*.insteadOf mechanism rewrites a
+   "git clone $URL" that refers to a repository over the network to a
+   clone from a local disk.
+
  * "git commit --date=<date>" option learned to read from more
    timestamp formats, including "--date=now".
 
@@ -93,7 +99,11 @@ UI, Workflows & Features
    users need to explicitly set the variable to 'true' if they want
    to resurrect the now-ignored use case.
 
- * "git replace" learned the "--edit" subcommand.
+ * "git replace" learned the "--edit" subcommand to create a
+   replacement by editing an existing object.
+
+ * "git replace" learned a "--graft" option to rewrite parents of a
+   commit.
 
  * "git send-email" learned "--to-cover" and "--cc-cover" options, to
    tell it to copy To: and Cc: headers found in the first input file
@@ -106,6 +116,10 @@ UI, Workflows & Features
  * "git tag" when editing the tag message shows the name of the tag
    being edited as a comment in the editor.
 
+ * "git tag" learned to pay attention to "tag.sort" configuration, to
+   be used as the default sort order when no --sort=<value> the option
+   is given.
+
  * "git verify-commit" command to check GPG signature in signed
    commits, in a way similar to "git verify-tag" is used to check
    signed tags, was added.
@@ -115,6 +129,14 @@ Performance, Internal Implementation, etc.
 
  * Build procedure for 'subtree' (in contrib/) has been cleaned up.
 
+ * The support for the profile-feedback build, which has been left
+   bit-rotten for quite a while, has been updated.
+
+ * An experimental format to use two files (the base file and
+   incremental changes relative to it) to represent the index has been
+   introduced; this may reduce I/O cost of rewriting a large index
+   when only small part of the working tree changes.
+
  * Effort to shrink the size of patches Windows folks maintain on top
    by upstreaming them continues.
 
@@ -141,6 +163,9 @@ Performance, Internal Implementation, etc.
    example, "update-ref --stdin [-z]" has been updated to use this
    API.
 
+ * skip_prefix() and strip_suffix() API functions are used a lot more
+   widely throughout the codebase now.
+
  * Parts of the test scripts can be skipped by using a range notation,
    e.g. "sh t1234-test.sh --run='1-4 6 8-'" to omit test piece 5 and 7
    and run everything else.
@@ -185,6 +210,7 @@ notes for details).
  * Code to avoid adding the same alternate object store twice was
    subtly broken for a long time, but nobody seems to have noticed.
    (merge 80b4785 rs/fix-alt-odb-path-comparison later to maint).
+   (merge 539e750 ek/alt-odb-entry-fix later to maint).
 
  * The "%<(10,trunc)%s" pretty format specifier in the log family of
    commands is used to truncate the string to a given length (e.g. 10
@@ -213,6 +239,16 @@ notes for details).
    couple of options unique to "git merge".
    (merge 8fee872 jk/complete-merge-pull later to maint).
 
+ * The unix-domain socket used by the sample credential cache daemon
+   tried to unlink an existing stale one at a wrong path, if the path
+   to the socket was given as an overlong path that does not fit in
+   sun_path member of the sockaddr_un structure.
+   (merge 2869b3e rs/fix-unlink-unix-socket later to maint).
+
+ * An ancient rewrite passed a wrong pointer to a curl library
+   function in a rarely used code path.
+   (merge 479eaa8 ah/fix-http-push later to maint).
+
  * "--ignore-space-change" option of "git apply" ignored the spaces
    at the beginning of line too aggressively, which is inconsistent
    with the option of the same name "diff" and "git diff" have.
@@ -242,6 +278,11 @@ notes for details).
    bit.
    (merge 5304810 jk/diff-files-assume-unchanged later to maint).
 
+ * "filter-branch" left an empty single-parent commit that results when
+   all parents of a merge commit gets mapped to the same commit, even
+   under "--prune-empty".
+   (merge 79bc4ef cb/filter-branch-prune-empty-degenerate-merges later to maint).
+
  * "git format-patch" did not enforce the rule that the "--follow"
    option from the log/diff family of commands must be used with
    exactly one pathspec.
@@ -267,6 +308,13 @@ notes for details).
    distinguish missing objects from type errors.
    (merge 77583e7 jk/index-pack-report-missing later to maint).
 
+ * "log --show-signature" incorrectly decided the color to paint a
+   mergetag that was and was not correctly validated.
+   (merge 42c55ce mg/fix-log-mergetag-color later to maint).
+
+ * "log --show-signature" did not pay attention to "--graph" option.
+   (merge cf3983d zk/log-graph-showsig later to maint).
+
  * "git mailinfo" used to read beyond the end of header string while
    parsing an incoming e-mail message to extract the patch.
    (merge b1a013d rs/mailinfo-header-cmp later to maint).
@@ -276,6 +324,11 @@ notes for details).
    except for case differences.
    (merge baa37bf dt/merge-recursive-case-insensitive later to maint).
 
+ * Merging changes into a file that ends in an incomplete line made the
+   last line into a complete one, even when the other branch did not
+   change anything around the end of file.
+   (merge ba31180 mk/merge-incomplete-files later to maint).
+
  * "git pack-objects" unnecessarily copied the previous contents when
    extending the hashtable, even though it will populate the table
    from scratch anyway.
@@ -306,6 +359,9 @@ notes for details).
    emptying the insn sheet.
    (merge ddb5432 rr/rebase-autostash-fix later to maint).
 
+ * "git rebase --fork-point" did not filter out patch-identical
+   commits correctly.
+
  * During "git rebase --merge", a conflicted patch could not be
    skipped with "--skip" if the next one also conflicted.
    (merge 95104c7 bc/fix-rebase-merge-skip later to maint).
index 1d718bdb9662735d9b446033607c5f2aa92f8a78..c55c22ab7be94e798164a48622aca21dd4daa44f 100644 (file)
@@ -2354,6 +2354,11 @@ submodule.<name>.ignore::
        "--ignore-submodules" option. The 'git submodule' commands are not
        affected by this setting.
 
+tag.sort::
+       This variable controls the sort ordering of tags when displayed by
+       linkgit:git-tag[1]. Without the "--sort=<value>" option provided, the
+       value of this variable will be used as the default.
+
 tar.umask::
        This variable can be used to restrict the permission bits of
        tar archive entries.  The default is 0002, which turns off the
index 61461b9f3399bd99547407ae0f3ccb4a38b77582..8fff598fd6e86c3e4a3e30e3c41f1c63ffb6a212 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 [verse]
 'git replace' [-f] <object> <replacement>
 'git replace' [-f] --edit <object>
+'git replace' [-f] --graft <commit> [<parent>...]
 'git replace' -d <object>...
 'git replace' [--format=<format>] [-l [<pattern>]]
 
@@ -73,6 +74,23 @@ OPTIONS
        newly created object. See linkgit:git-var[1] for details about
        how the editor will be chosen.
 
+--raw::
+       When editing, provide the raw object contents rather than
+       pretty-printed ones. Currently this only affects trees, which
+       will be shown in their binary form. This is harder to work with,
+       but can help when repairing a tree that is so corrupted it
+       cannot be pretty-printed. Note that you may need to configure
+       your editor to cleanly read and write binary data.
+
+--graft <commit> [<parent>...]::
+       Create a graft commit. A new commit is created with the same
+       content as <commit> except that its parents will be
+       [<parent>...] instead of <commit>'s parents. A replacement ref
+       is then created to replace <commit> with the newly created
+       commit. See contrib/convert-grafts-to-replace-refs.sh for an
+       example script based on this option that can convert grafts to
+       replace refs.
+
 -l <pattern>::
 --list <pattern>::
        List replace refs for objects that match the given pattern (or
index 987395d22af915832c535840ef59927fc5125294..0b84769bd91b5894a246ff3d499dca1ffba339ac 100644 (file)
@@ -102,7 +102,7 @@ eval "set -- $(git rev-parse --sq --prefix "$prefix" "$@")"
 +
 If you want to make sure that the output actually names an object in
 your object database and/or can be used as a specific type of object
-you require, you can add "^{type}" peeling operator to the parameter.
+you require, you can add "\^{type}" peeling operator to the parameter.
 For example, `git rev-parse "$VAR^{commit}"` will make sure `$VAR`
 names an existing object that is a commit-ish (i.e. a commit, or an
 annotated tag that points at a commit).  To make sure that `$VAR`
@@ -245,6 +245,10 @@ print a message to stderr and exit with nonzero status.
 --show-toplevel::
        Show the absolute path of the top-level directory.
 
+--shared-index-path::
+       Show the path to the shared index file in split index mode, or
+       empty if not in split-index mode.
+
 Other Options
 ~~~~~~~~~~~~~
 
index b424a1bc48bba96513da17ee63bee4383453c3b4..320908369f067f5d494c6bc0c3c9f28fa26859a6 100644 (file)
@@ -99,7 +99,9 @@ OPTIONS
        Sort in a specific order. Supported type is "refname"
        (lexicographic order), "version:refname" or "v:refname" (tag
        names are treated as versions). Prepend "-" to reverse sort
-       order.
+       order. When this option is not given, the sort order defaults to the
+       value configured for the 'tag.sort' variable if it exists, or
+       lexicographic order otherwise. See linkgit:git-config[1].
 
 --column[=<options>]::
 --no-column::
@@ -317,6 +319,7 @@ include::date-formats.txt[]
 SEE ALSO
 --------
 linkgit:git-check-ref-format[1].
+linkgit:git-config[1].
 
 GIT
 ---
index d6de4a008ce74b9aa3f83035b4392e8d4505fae7..dfc09d93d85f5ca598447e98327518082c8e1a16 100644 (file)
@@ -161,6 +161,17 @@ may not support it yet.
        Only meaningful with `--stdin` or `--index-info`; paths are
        separated with NUL character instead of LF.
 
+--split-index::
+--no-split-index::
+       Enable or disable split index mode. If enabled, the index is
+       split into two files, $GIT_DIR/index and $GIT_DIR/sharedindex.<SHA-1>.
+       Changes are accumulated in $GIT_DIR/index while the shared
+       index file contains all index entries stays unchanged. If
+       split-index mode is already enabled and `--split-index` is
+       given again, all changes in $GIT_DIR/index are pushed back to
+       the shared index file. This mode is designed for very large
+       indexes that take a signficant amount of time to read or write.
+
 \--::
        Do not interpret any more arguments as options.
 
index 79242096714e90a385e9b63a60326a41f8bd983d..a0f1ede86603b181c09dc89bc1a9e634c92b273a 100644 (file)
@@ -43,9 +43,11 @@ unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v2.0.1/git.html[documentation for release 2.0.1]
+* link:v2.0.3/git.html[documentation for release 2.0.3]
 
 * release notes for
+  link:RelNotes/2.0.3.txt[2.0.3],
+  link:RelNotes/2.0.2.txt[2.0.2],
   link:RelNotes/2.0.1.txt[2.0.1],
   link:RelNotes/2.0.0.txt[2.0.0].
 
@@ -905,31 +907,54 @@ for further details.
        based on whether stdout appears to be redirected to a file or not.
 
 'GIT_TRACE'::
-       If this variable is set to "1", "2" or "true" (comparison
-       is case insensitive), Git will print `trace:` messages on
-       stderr telling about alias expansion, built-in command
-       execution and external command execution.
-       If this variable is set to an integer value greater than 1
-       and lower than 10 (strictly) then Git will interpret this
-       value as an open file descriptor and will try to write the
-       trace messages into this file descriptor.
-       Alternatively, if this variable is set to an absolute path
-       (starting with a '/' character), Git will interpret this
-       as a file path and will try to write the trace messages
-       into it.
+       Enables general trace messages, e.g. alias expansion, built-in
+       command execution and external command execution.
++
+If this variable is set to "1", "2" or "true" (comparison
+is case insensitive), trace messages will be printed to
+stderr.
++
+If the variable is set to an integer value greater than 2
+and lower than 10 (strictly) then Git will interpret this
+value as an open file descriptor and will try to write the
+trace messages into this file descriptor.
++
+Alternatively, if the variable is set to an absolute path
+(starting with a '/' character), Git will interpret this
+as a file path and will try to write the trace messages
+into it.
++
+Unsetting the variable, or setting it to empty, "0" or
+"false" (case insensitive) disables trace messages.
 
 'GIT_TRACE_PACK_ACCESS'::
-       If this variable is set to a path, a file will be created at
-       the given path logging all accesses to any packs. For each
+       Enables trace messages for all accesses to any packs. For each
        access, the pack file name and an offset in the pack is
        recorded. This may be helpful for troubleshooting some
        pack-related performance problems.
+       See 'GIT_TRACE' for available trace output options.
 
 'GIT_TRACE_PACKET'::
-       If this variable is set, it shows a trace of all packets
-       coming in or out of a given program. This can help with
-       debugging object negotiation or other protocol issues. Tracing
-       is turned off at a packet starting with "PACK".
+       Enables trace messages for all packets coming in or out of a
+       given program. This can help with debugging object negotiation
+       or other protocol issues. Tracing is turned off at a packet
+       starting with "PACK".
+       See 'GIT_TRACE' for available trace output options.
+
+'GIT_TRACE_PERFORMANCE'::
+       Enables performance related trace messages, e.g. total execution
+       time of each Git command.
+       See 'GIT_TRACE' for available trace output options.
+
+'GIT_TRACE_SETUP'::
+       Enables trace messages printing the .git, working tree and current
+       working directory after Git has completed its setup phase.
+       See 'GIT_TRACE' for available trace output options.
+
+'GIT_TRACE_SHALLOW'::
+       Enables trace messages that can help debugging fetching /
+       cloning of shallow repositories.
+       See 'GIT_TRACE' for available trace output options.
 
 GIT_LITERAL_PATHSPECS::
        Setting this variable to `1` will cause Git to treat all
@@ -1043,7 +1068,7 @@ Authors
 -------
 Git was started by Linus Torvalds, and is currently maintained by Junio
 C Hamano. Numerous contributions have come from the Git mailing list
-<git@vger.kernel.org>.  http://www.ohloh.net/p/git/contributors/summary
+<git@vger.kernel.org>.  http://www.openhub.net/p/git/contributors/summary
 gives you a more complete list of contributors.
 
 If you have a clone of git.git itself, the
index 17d2ea6c1e48733d85a8caa49f6143c76edb09aa..79653f313474fa658f072c97d8744e5afccb77d7 100644 (file)
@@ -155,6 +155,10 @@ index::
        The current index file for the repository.  It is
        usually not found in a bare repository.
 
+sharedindex.<SHA-1>::
+       The shared index part, to be referenced by $GIT_DIR/index and
+       other temporary index files. Only valid in split index mode.
+
 info::
        Additional information about the repository is recorded
        in this directory.
index b977ae8bbb1f46ebb40d1d315e79262fe344f1c2..ad7a5bddd24d91ceda78d430fd86a204b21fd005 100644 (file)
@@ -8,11 +8,19 @@ Data Structures
 
 `struct hashmap`::
 
-       The hash table structure.
+       The hash table structure. Members can be used as follows, but should
+       not be modified directly:
 +
-The `size` member keeps track of the total number of entries. The `cmpfn`
-member is a function used to compare two entries for equality. The `table` and
-`tablesize` members store the hash table and its size, respectively.
+The `size` member keeps track of the total number of entries (0 means the
+hashmap is empty).
++
+`tablesize` is the allocated size of the hash table. A non-0 value indicates
+that the hashmap is initialized. It may also be useful for statistical purposes
+(i.e. `size / tablesize` is the current load factor).
++
+`cmpfn` stores the comparison function specified in `hashmap_init()`. In
+advanced scenarios, it may be useful to change this, e.g. to switch between
+case-sensitive and case-insensitive lookup.
 
 `struct hashmap_entry`::
 
@@ -58,6 +66,15 @@ Functions
 +
 `strihash` and `memihash` are case insensitive versions.
 
+`unsigned int sha1hash(const unsigned char *sha1)`::
+
+       Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
+       for use in hash tables. Cryptographic hashes are supposed to have
+       uniform distribution, so in contrast to `memhash()`, this just copies
+       the first `sizeof(int)` bytes without shuffling any bits. Note that
+       the results will be different on big-endian and little-endian
+       platforms, so they should not be stored or transferred over the net.
+
 `void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function, size_t initial_size)`::
 
        Initializes a hashmap structure.
@@ -101,6 +118,20 @@ hashmap_entry) that has at least been initialized with the proper hash code
 If an entry with matching hash code is found, `key` and `keydata` are passed
 to `hashmap_cmp_fn` to decide whether the entry matches the key.
 
+`void *hashmap_get_from_hash(const struct hashmap *map, unsigned int hash, const void *keydata)`::
+
+       Returns the hashmap entry for the specified hash code and key data,
+       or NULL if not found.
++
+`map` is the hashmap structure.
++
+`hash` is the hash code of the entry to look up.
++
+If an entry with matching hash code is found, `keydata` is passed to
+`hashmap_cmp_fn` to decide whether the entry matches the key. The
+`entry_or_key` parameter points to a bogus hashmap_entry structure that
+should not be used in the comparison.
+
 `void *hashmap_get_next(const struct hashmap *map, const void *entry)`::
 
        Returns the next equal hashmap entry, or NULL if not found. This can be
@@ -162,6 +193,21 @@ more entries.
 `hashmap_iter_first` is a combination of both (i.e. initializes the iterator
 and returns the first entry, if any).
 
+`const char *strintern(const char *string)`::
+`const void *memintern(const void *data, size_t len)`::
+
+       Returns the unique, interned version of the specified string or data,
+       similar to the `String.intern` API in Java and .NET, respectively.
+       Interned strings remain valid for the entire lifetime of the process.
++
+Can be used as `[x]strdup()` or `xmemdupz` replacement, except that interned
+strings / data must not be modified or freed.
++
+Interned strings are best used for short strings with high probability of
+duplicates.
++
+Uses a hashmap to store the pool of interned strings.
+
 Usage example
 -------------
 
index f1add51efeefb31e1da96a5ee62871e239819ebf..d51a6579c85f7417708f918cf9807447b1508e3c 100644 (file)
@@ -68,6 +68,11 @@ Functions
 
 * General ones (works with sorted and unsorted lists as well)
 
+`string_list_init`::
+
+       Initialize the members of the string_list, set `strdup_strings`
+       member according to the value of the second parameter.
+
 `filter_string_list`::
 
        Apply a function to each item in a list, retaining only the
diff --git a/Documentation/technical/api-trace.txt b/Documentation/technical/api-trace.txt
new file mode 100644 (file)
index 0000000..097a651
--- /dev/null
@@ -0,0 +1,97 @@
+trace API
+=========
+
+The trace API can be used to print debug messages to stderr or a file. Trace
+code is inactive unless explicitly enabled by setting `GIT_TRACE*` environment
+variables.
+
+The trace implementation automatically adds `timestamp file:line ... \n` to
+all trace messages. E.g.:
+
+------------
+23:59:59.123456 git.c:312               trace: built-in: git 'foo'
+00:00:00.000001 builtin/foo.c:99        foo: some message
+------------
+
+Data Structures
+---------------
+
+`struct trace_key`::
+
+       Defines a trace key (or category). The default (for API functions that
+       don't take a key) is `GIT_TRACE`.
++
+E.g. to define a trace key controlled by environment variable `GIT_TRACE_FOO`:
++
+------------
+static struct trace_key trace_foo = TRACE_KEY_INIT(FOO);
+
+static void trace_print_foo(const char *message)
+{
+       trace_print_key(&trace_foo, message);
+}
+------------
++
+Note: don't use `const` as the trace implementation stores internal state in
+the `trace_key` structure.
+
+Functions
+---------
+
+`int trace_want(struct trace_key *key)`::
+
+       Checks whether the trace key is enabled. Used to prevent expensive
+       string formatting before calling one of the printing APIs.
+
+`void trace_disable(struct trace_key *key)`::
+
+       Disables tracing for the specified key, even if the environment
+       variable was set.
+
+`void trace_printf(const char *format, ...)`::
+`void trace_printf_key(struct trace_key *key, const char *format, ...)`::
+
+       Prints a formatted message, similar to printf.
+
+`void trace_argv_printf(const char **argv, const char *format, ...)``::
+
+       Prints a formatted message, followed by a quoted list of arguments.
+
+`void trace_strbuf(struct trace_key *key, const struct strbuf *data)`::
+
+       Prints the strbuf, without additional formatting (i.e. doesn't
+       choke on `%` or even `\0`).
+
+`uint64_t getnanotime(void)`::
+
+       Returns nanoseconds since the epoch (01/01/1970), typically used
+       for performance measurements.
++
+Currently there are high precision timer implementations for Linux (using
+`clock_gettime(CLOCK_MONOTONIC)`) and Windows (`QueryPerformanceCounter`).
+Other platforms use `gettimeofday` as time source.
+
+`void trace_performance(uint64_t nanos, const char *format, ...)`::
+`void trace_performance_since(uint64_t start, const char *format, ...)`::
+
+       Prints the elapsed time (in nanoseconds), or elapsed time since
+       `start`, followed by a formatted message. Enabled via environment
+       variable `GIT_TRACE_PERFORMANCE`. Used for manual profiling, e.g.:
++
+------------
+uint64_t start = getnanotime();
+/* code section to measure */
+trace_performance_since(start, "foobar");
+------------
++
+------------
+uint64_t t = 0;
+for (;;) {
+       /* ignore */
+       t -= getnanotime();
+       /* code section to measure */
+       t += getnanotime();
+       /* ignore */
+}
+trace_performance(t, "frotz");
+------------
index f352a9b22e3138fa674c393d99d420de4f2ff144..fe6f31667d7d5eba34c57e322b557ab29d618011 100644 (file)
@@ -129,6 +129,9 @@ Git index format
   (Version 4) In version 4, the padding after the pathname does not
   exist.
 
+  Interpretation of index entries in split index mode is completely
+  different. See below for details.
+
 == Extensions
 
 === Cached tree
@@ -198,3 +201,35 @@ Git index format
   - At most three 160-bit object names of the entry in stages from 1 to 3
     (nothing is written for a missing stage).
 
+=== Split index
+
+  In split index mode, the majority of index entries could be stored
+  in a separate file. This extension records the changes to be made on
+  top of that to produce the final index.
+
+  The signature for this extension is { 'l', 'i, 'n', 'k' }.
+
+  The extension consists of:
+
+  - 160-bit SHA-1 of the shared index file. The shared index file path
+    is $GIT_DIR/sharedindex.<SHA-1>. If all 160 bits are zero, the
+    index does not require a shared index file.
+
+  - An ewah-encoded delete bitmap, each bit represents an entry in the
+    shared index. If a bit is set, its corresponding entry in the
+    shared index will be removed from the final index.  Note, because
+    a delete operation changes index entry positions, but we do need
+    original positions in replace phase, it's best to just mark
+    entries for removal, then do a mass deletion after replacement.
+
+  - An ewah-encoded replace bitmap, each bit represents an entry in
+    the shared index. If a bit is set, its corresponding entry in the
+    shared index will be replaced with an entry in this index
+    file. All replaced entries are stored in sorted order in this
+    index. The first "1" bit in the replace bitmap corresponds to the
+    first index entry, the second "1" bit to the second entry and so
+    on. Replaced entries may have empty path names to save space.
+
+  The remaining index entries after replaced ones will be added to the
+  final index. These added entries are also sorted by entry namme then
+  stage.
index 40adbf7bf79ad7d7800b71d2ca79418cdbddd3ed..f1bae1ce37c2ea54a02442d1f0f3cdec7c7d656a 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.0.0.GIT
+DEF_VER=v2.1.0-rc0
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index ba01e7421eb892cfe2b86c4b26786ae9e5bb00af..6ec7a24e1a7ff1d9bb380e6c9bdb1c16dbf5d1c1 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -28,7 +28,7 @@ set up install paths (via config.mak.autogen), so you can write instead
 If you're willing to trade off (much) longer build time for a later
 faster git you can also do a profile feedback build with
 
-       $ make prefix=/usr PROFILE=BUILD all
+       $ make prefix=/usr profile
        # make prefix=/usr PROFILE=BUILD install
 
 This will run the complete test suite as training workload and then
@@ -36,10 +36,20 @@ rebuild git with the generated profile feedback. This results in a git
 which is a few percent faster on CPU intensive workloads.  This
 may be a good tradeoff for distribution packagers.
 
+Alternatively you can run profile feedback only with the git benchmark
+suite. This runs significantly faster than the full test suite, but
+has less coverage:
+
+       $ make prefix=/usr profile-fast
+       # make prefix=/usr PROFILE=BUILD install
+
 Or if you just want to install a profile-optimized version of git into
 your home directory, you could run:
 
-       $ make PROFILE=BUILD install
+       $ make profile-install
+
+or
+       $ make profile-fast-install
 
 As a caveat: a profile-optimized build takes a *lot* longer since the
 git tree must be built twice, and in order for the profiling
index b92418de7bc743a70f6113cb2af39c74a3cb312c..2320de592e6dbc545866e6bfef09a05f660c2c14 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -340,6 +340,8 @@ all::
 #
 # Define GMTIME_UNRELIABLE_ERRORS if your gmtime() function does not
 # return NULL when it receives a bogus time_t.
+#
+# Define HAVE_CLOCK_GETTIME if your platform has clock_gettime in librt.
 
 GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -552,6 +554,7 @@ TEST_PROGRAMS_NEED_X += test-ctype
 TEST_PROGRAMS_NEED_X += test-date
 TEST_PROGRAMS_NEED_X += test-delta
 TEST_PROGRAMS_NEED_X += test-dump-cache-tree
+TEST_PROGRAMS_NEED_X += test-dump-split-index
 TEST_PROGRAMS_NEED_X += test-genrandom
 TEST_PROGRAMS_NEED_X += test-hashmap
 TEST_PROGRAMS_NEED_X += test-index-version
@@ -875,6 +878,7 @@ LIB_OBJS += sha1_name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
 LIB_OBJS += sigchain.o
+LIB_OBJS += split-index.o
 LIB_OBJS += strbuf.o
 LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
@@ -1498,6 +1502,11 @@ ifdef GMTIME_UNRELIABLE_ERRORS
        BASIC_CFLAGS += -DGMTIME_UNRELIABLE_ERRORS
 endif
 
+ifdef HAVE_CLOCK_GETTIME
+       BASIC_CFLAGS += -DHAVE_CLOCK_GETTIME
+       EXTLIBS += -lrt
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK = NoThanks
 endif
@@ -1553,13 +1562,13 @@ endif
 PROFILE_DIR := $(CURDIR)
 
 ifeq ("$(PROFILE)","GEN")
-       CFLAGS += -fprofile-generate=$(PROFILE_DIR) -DNO_NORETURN=1
+       BASIC_CFLAGS += -fprofile-generate=$(PROFILE_DIR) -DNO_NORETURN=1
        EXTLIBS += -lgcov
        export CCACHE_DISABLE = t
        V = 1
 else
 ifneq ("$(PROFILE)","")
-       CFLAGS += -fprofile-use=$(PROFILE_DIR) -fprofile-correction -DNO_NORETURN=1
+       BASIC_CFLAGS += -fprofile-use=$(PROFILE_DIR) -fprofile-correction -DNO_NORETURN=1
        export CCACHE_DISABLE = t
        V = 1
 endif
@@ -1644,12 +1653,20 @@ SHELL = $(SHELL_PATH)
 all:: shell_compatibility_test
 
 ifeq "$(PROFILE)" "BUILD"
-ifeq ($(filter all,$(MAKECMDGOALS)),all)
-all:: profile-clean
+all:: profile
+endif
+
+profile:: profile-clean
        $(MAKE) PROFILE=GEN all
        $(MAKE) PROFILE=GEN -j1 test
-endif
-endif
+       $(MAKE) PROFILE=GEN -j1 perf
+       $(MAKE) PROFILE=USE all
+
+profile-fast: profile-clean
+       $(MAKE) PROFILE=GEN all
+       $(MAKE) PROFILE=GEN -j1 perf
+       $(MAKE) PROFILE=USE all
+
 
 all:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
 ifneq (,$X)
@@ -2336,6 +2353,12 @@ mergetools_instdir_SQ = $(subst ','\'',$(mergetools_instdir))
 
 install_bindir_programs := $(patsubst %,%$X,$(BINDIR_PROGRAMS_NEED_X)) $(BINDIR_PROGRAMS_NO_X)
 
+profile-install: profile
+       $(MAKE) install
+
+profile-fast-install: profile-fast
+       $(MAKE) install
+
 install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
diff --git a/alloc.c b/alloc.c
index eb22a45c9d0acf08a6024094bd0a2090a7307945..12afadfacdd6094912a6e18a217a9aa6318b47b2 100644 (file)
--- a/alloc.c
+++ b/alloc.c
 
 #define BLOCKING 1024
 
-#define DEFINE_ALLOCATOR(name, type)                           \
-static unsigned int name##_allocs;                             \
-void *alloc_##name##_node(void)                                        \
-{                                                              \
-       static int nr;                                          \
-       static type *block;                                     \
-       void *ret;                                              \
-                                                               \
-       if (!nr) {                                              \
-               nr = BLOCKING;                                  \
-               block = xmalloc(BLOCKING * sizeof(type));       \
-       }                                                       \
-       nr--;                                                   \
-       name##_allocs++;                                        \
-       ret = block++;                                          \
-       memset(ret, 0, sizeof(type));                           \
-       return ret;                                             \
-}
-
 union any_object {
        struct object object;
        struct blob blob;
@@ -45,17 +26,73 @@ union any_object {
        struct tag tag;
 };
 
-DEFINE_ALLOCATOR(blob, struct blob)
-DEFINE_ALLOCATOR(tree, struct tree)
-DEFINE_ALLOCATOR(raw_commit, struct commit)
-DEFINE_ALLOCATOR(tag, struct tag)
-DEFINE_ALLOCATOR(object, union any_object)
+struct alloc_state {
+       int count; /* total number of nodes allocated */
+       int nr;    /* number of nodes left in current allocation */
+       void *p;   /* first free node in current allocation */
+};
+
+static inline void *alloc_node(struct alloc_state *s, size_t node_size)
+{
+       void *ret;
+
+       if (!s->nr) {
+               s->nr = BLOCKING;
+               s->p = xmalloc(BLOCKING * node_size);
+       }
+       s->nr--;
+       s->count++;
+       ret = s->p;
+       s->p = (char *)s->p + node_size;
+       memset(ret, 0, node_size);
+       return ret;
+}
+
+static struct alloc_state blob_state;
+void *alloc_blob_node(void)
+{
+       struct blob *b = alloc_node(&blob_state, sizeof(struct blob));
+       b->object.type = OBJ_BLOB;
+       return b;
+}
+
+static struct alloc_state tree_state;
+void *alloc_tree_node(void)
+{
+       struct tree *t = alloc_node(&tree_state, sizeof(struct tree));
+       t->object.type = OBJ_TREE;
+       return t;
+}
+
+static struct alloc_state tag_state;
+void *alloc_tag_node(void)
+{
+       struct tag *t = alloc_node(&tag_state, sizeof(struct tag));
+       t->object.type = OBJ_TAG;
+       return t;
+}
+
+static struct alloc_state object_state;
+void *alloc_object_node(void)
+{
+       struct object *obj = alloc_node(&object_state, sizeof(union any_object));
+       obj->type = OBJ_NONE;
+       return obj;
+}
+
+static struct alloc_state commit_state;
+
+unsigned int alloc_commit_index(void)
+{
+       static unsigned int count;
+       return count++;
+}
 
 void *alloc_commit_node(void)
 {
-       static int commit_count;
-       struct commit *c = alloc_raw_commit_node();
-       c->index = commit_count++;
+       struct commit *c = alloc_node(&commit_state, sizeof(struct commit));
+       c->object.type = OBJ_COMMIT;
+       c->index = alloc_commit_index();
        return c;
 }
 
@@ -66,13 +103,13 @@ static void report(const char *name, unsigned int count, size_t size)
 }
 
 #define REPORT(name, type)     \
-    report(#name, name##_allocs, name##_allocs * sizeof(type) >> 10)
+    report(#name, name##_state.count, name##_state.count * sizeof(type) >> 10)
 
 void alloc_report(void)
 {
        REPORT(blob, struct blob);
        REPORT(tree, struct tree);
-       REPORT(raw_commit, struct commit);
+       REPORT(commit, struct commit);
        REPORT(tag, struct tag);
        REPORT(object, union any_object);
 }
diff --git a/blob.c b/blob.c
index ae320bd8fa22aaaef8144bd6bc35c20d1e85e4f4..1fcb8e44b00f3b9eedbe92da8abe47c2bebca7c7 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -7,15 +7,8 @@ struct blob *lookup_blob(const unsigned char *sha1)
 {
        struct object *obj = lookup_object(sha1);
        if (!obj)
-               return create_object(sha1, OBJ_BLOB, alloc_blob_node());
-       if (!obj->type)
-               obj->type = OBJ_BLOB;
-       if (obj->type != OBJ_BLOB) {
-               error("Object %s is a %s, not a blob",
-                     sha1_to_hex(sha1), typename(obj->type));
-               return NULL;
-       }
-       return (struct blob *) obj;
+               return create_object(sha1, alloc_blob_node());
+       return object_as_type(obj, OBJ_BLOB, 0);
 }
 
 int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
index 459208a326cd4cebfa813168d81a06c27fc54fc5..4baf3a563510b10e592ff06ae62df2f405413f6c 100644 (file)
@@ -299,7 +299,6 @@ static int add_files(struct dir_struct *dir, int flags)
 int cmd_add(int argc, const char **argv, const char *prefix)
 {
        int exit_status = 0;
-       int newfd;
        struct pathspec pathspec;
        struct dir_struct dir;
        int flags;
@@ -345,7 +344,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        add_new_files = !take_worktree_changes && !refresh_only;
        require_pathspec = !take_worktree_changes;
 
-       newfd = hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, 1);
 
        flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
                 (show_only ? ADD_CACHE_PRETEND : 0) |
@@ -443,8 +442,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
 finish:
        if (active_cache_changed) {
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(&lock_file))
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
 
index fc43eed36b55e4966796490b8c0a02fae790229c..da413ae0d178b53d6b1e6c193577c23923255245 100644 (file)
@@ -5,20 +5,18 @@
  */
 #include "git-compat-util.h"
 #include "builtin.h"
+#include "argv-array.h"
 
 int cmd_annotate(int argc, const char **argv, const char *prefix)
 {
-       const char **nargv;
+       struct argv_array args = ARGV_ARRAY_INIT;
        int i;
-       nargv = xmalloc(sizeof(char *) * (argc + 2));
 
-       nargv[0] = "annotate";
-       nargv[1] = "-c";
+       argv_array_pushl(&args, "annotate", "-c", NULL);
 
        for (i = 1; i < argc; i++) {
-               nargv[i+1] = argv[i];
+               argv_array_push(&args, argv[i]);
        }
-       nargv[argc + 1] = NULL;
 
-       return cmd_blame(argc + 1, nargv, prefix);
+       return cmd_blame(args.argc, args.argv, prefix);
 }
index 16cc93587fd78f620bdcc0d0841ca98ac3394d9e..be2b4ce2fd91640379b0a65d0aa90238d7edfd64 100644 (file)
@@ -1075,7 +1075,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
 
        line = ptr + 2;
        ptr = strchr(line, ' ');
-       eol = strchr(line, '\n');
+       eol = strchrnul(line, '\n');
 
        if (!ptr || eol < ptr)
                ptr = eol;
@@ -2867,9 +2867,7 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
        case BINARY_LITERAL_DEFLATED:
                clear_image(img);
                img->len = fragment->size;
-               img->buf = xmalloc(img->len+1);
-               memcpy(img->buf, fragment->patch, img->len);
-               img->buf[img->len] = '\0';
+               img->buf = xmemdupz(fragment->patch, img->len);
                return 0;
        }
        return -1;
@@ -3084,13 +3082,15 @@ static void prepare_fn_table(struct patch *patch)
        }
 }
 
-static int checkout_target(struct cache_entry *ce, struct stat *st)
+static int checkout_target(struct index_state *istate,
+                          struct cache_entry *ce, struct stat *st)
 {
        struct checkout costate;
 
        memset(&costate, 0, sizeof(costate));
        costate.base_dir = "";
        costate.refresh_cache = 1;
+       costate.istate = istate;
        if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
                return error(_("cannot checkout %s"), ce->name);
        return 0;
@@ -3257,7 +3257,7 @@ static int load_current(struct image *image, struct patch *patch)
        if (lstat(name, &st)) {
                if (errno != ENOENT)
                        return error(_("%s: %s"), name, strerror(errno));
-               if (checkout_target(ce, &st))
+               if (checkout_target(&the_index, ce, &st))
                        return -1;
        }
        if (verify_index_match(ce, &st))
@@ -3411,7 +3411,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                }
                *ce = active_cache[pos];
                if (stat_ret < 0) {
-                       if (checkout_target(*ce, st))
+                       if (checkout_target(&the_index, *ce, st))
                                return -1;
                }
                if (!cached && verify_index_match(*ce, st))
@@ -3644,7 +3644,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
 {
        struct patch *patch;
        struct index_state result = { NULL };
-       int fd;
+       static struct lock_file lock;
 
        /* Once we start supporting the reverse patch, it may be
         * worth showing the new sha1 prefix, but until then...
@@ -3682,8 +3682,8 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
                        die ("Could not add %s to temporary index", name);
        }
 
-       fd = open(filename, O_WRONLY | O_CREAT, 0666);
-       if (fd < 0 || write_index(&result, fd) || close(fd))
+       hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+       if (write_locked_index(&result, &lock, COMMIT_LOCK))
                die ("Could not write temporary index to %s", filename);
 
        discard_index(&result);
@@ -4502,8 +4502,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
        }
 
        if (update_index) {
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(&lock_file))
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
 
index d3b256e545a76b096682223539c4faec8d770b2f..17d30d00aa3f6c4d0f62cae6603f9003dd2f2395 100644 (file)
@@ -1371,11 +1371,8 @@ static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit
 
 static int num_scapegoats(struct rev_info *revs, struct commit *commit)
 {
-       int cnt;
        struct commit_list *l = first_scapegoat(revs, commit);
-       for (cnt = 0; l; l = l->next)
-               cnt++;
-       return cnt;
+       return commit_list_count(l);
 }
 
 /* Distribute collected unsorted blames to the respected sorted lists
@@ -2287,7 +2284,6 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        commit = alloc_commit_node();
        commit->object.parsed = 1;
        commit->date = now;
-       commit->object.type = OBJ_COMMIT;
        parent_tail = &commit->parents;
 
        if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
@@ -2389,7 +2385,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
         * right now, but someday we might optimize diff-index --cached
         * with cache-tree information.
         */
-       cache_tree_invalidate_path(active_cache_tree, path);
+       cache_tree_invalidate_path(&the_index, path);
 
        return commit;
 }
@@ -2707,11 +2703,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                die("revision walk setup failed");
 
        if (is_null_sha1(sb.final->object.sha1)) {
-               char *buf;
                o = sb.final->util;
-               buf = xmalloc(o->file.size + 1);
-               memcpy(buf, o->file.ptr, o->file.size + 1);
-               sb.final_buf = buf;
+               sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
                sb.final_buf_size = o->file.size;
        }
        else {
index 61e75eb60c992e38ddf195abad564a76cbf934cb..05edd9e1df52e63a1644cddc0fb0e6a0d764d90b 100644 (file)
@@ -135,6 +135,7 @@ static int option_parse_u(const struct option *opt,
        int *newfd = opt->value;
 
        state.refresh_cache = 1;
+       state.istate = &the_index;
        if (*newfd < 0)
                *newfd = hold_locked_index(&lock_file, 1);
        return 0;
@@ -279,8 +280,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                checkout_all(prefix, prefix_length);
 
        if (0 <= newfd &&
-           (write_cache(newfd, active_cache, active_nr) ||
-            commit_locked_index(&lock_file)))
+           write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die("Unable to write new index file");
        return 0;
 }
index 463cfeea5067fe02d8eb1414018c97ff9a757176..f71e74531d2a7d195ff2987f6dca3c69c636aa7c 100644 (file)
@@ -225,7 +225,6 @@ static int checkout_paths(const struct checkout_opts *opts,
        int flag;
        struct commit *head;
        int errs = 0;
-       int newfd;
        struct lock_file *lock_file;
 
        if (opts->track != BRANCH_TRACK_UNSPECIFIED)
@@ -256,7 +255,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       newfd = hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, 1);
        if (read_cache_preload(&opts->pathspec) < 0)
                return error(_("corrupt index file"));
 
@@ -337,6 +336,7 @@ static int checkout_paths(const struct checkout_opts *opts,
        memset(&state, 0, sizeof(state));
        state.force = 1;
        state.refresh_cache = 1;
+       state.istate = &the_index;
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
                if (ce->ce_flags & CE_MATCHED) {
@@ -352,8 +352,7 @@ static int checkout_paths(const struct checkout_opts *opts,
                }
        }
 
-       if (write_cache(newfd, active_cache, active_nr) ||
-           commit_locked_index(lock_file))
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        read_ref_full("HEAD", rev, 0, &flag);
@@ -444,8 +443,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
 {
        int ret;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-       int newfd = hold_locked_index(lock_file, 1);
 
+       hold_locked_index(lock_file, 1);
        if (read_cache_preload(NULL) < 0)
                return error(_("corrupt index file"));
 
@@ -553,8 +552,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
                }
        }
 
-       if (write_cache(newfd, active_cache, active_nr) ||
-           commit_locked_index(lock_file))
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        if (!opts->force && !opts->quiet)
index 27701d222c78ed84691b65334a7909f90c589464..1032563e5fae880df9256c9eafaa96d60462ecd2 100644 (file)
@@ -621,8 +621,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
                                nr += chosen[i];
                }
 
-               result = xmalloc(sizeof(int) * (nr + 1));
-               memset(result, 0, sizeof(int) * (nr + 1));
+               result = xcalloc(nr + 1, sizeof(int));
                for (i = 0; i < stuff->nr && j < nr; i++) {
                        if (chosen[i])
                                result[j++] = i;
index e15ca332b5637421fb187ce4db478c60dded958c..bbd169ceb49b2e09197e469aeb572e9aa54d4b85 100644 (file)
@@ -617,7 +617,7 @@ static int checkout(void)
        struct unpack_trees_options opts;
        struct tree *tree;
        struct tree_desc t;
-       int err = 0, fd;
+       int err = 0;
 
        if (option_no_checkout)
                return 0;
@@ -641,7 +641,7 @@ static int checkout(void)
        setup_work_tree();
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
-       fd = hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, 1);
 
        memset(&opts, 0, sizeof opts);
        opts.update = 1;
@@ -657,8 +657,7 @@ static int checkout(void)
        if (unpack_trees(1, &t, &opts) < 0)
                die(_("unable to checkout working tree"));
 
-       if (write_cache(fd, active_cache, active_nr) ||
-           commit_locked_index(lock_file))
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
@@ -800,18 +799,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                die(_("repository '%s' does not exist"), repo_name);
        else
                repo = repo_name;
-       is_local = option_local != 0 && path && !is_bundle;
-       if (is_local) {
-               if (option_depth)
-                       warning(_("--depth is ignored in local clones; use file:// instead."));
-               if (!access(mkpath("%s/shallow", path), F_OK)) {
-                       if (option_local > 0)
-                               warning(_("source repository is shallow, ignoring --local"));
-                       is_local = 0;
-               }
-       }
-       if (option_local > 0 && !is_local)
-               warning(_("--local is ignored"));
 
        /* no need to be strict, transport_set_option() will validate it again */
        if (option_depth && atoi(option_depth) < 1)
@@ -904,6 +891,19 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        remote = remote_get(option_origin);
        transport = transport_get(remote, remote->url[0]);
+       path = get_repo_path(remote->url[0], &is_bundle);
+       is_local = option_local != 0 && path && !is_bundle;
+       if (is_local) {
+               if (option_depth)
+                       warning(_("--depth is ignored in local clones; use file:// instead."));
+               if (!access(mkpath("%s/shallow", path), F_OK)) {
+                       if (option_local > 0)
+                               warning(_("source repository is shallow, ignoring --local"));
+                       is_local = 0;
+               }
+       }
+       if (option_local > 0 && !is_local)
+               warning(_("--local is ignored"));
        transport->cloning = 1;
 
        if (!transport->get_refs_list || (!is_local && !transport->fetch))
index 461c3b1cade4a995d3eb3cc212645791245865e0..5ed60364ce5eb1f458e1f5155d76abd7341d24ea 100644 (file)
@@ -305,7 +305,6 @@ static void refresh_cache_or_die(int refresh_flags)
 static char *prepare_index(int argc, const char **argv, const char *prefix,
                           const struct commit *current_head, int is_status)
 {
-       int fd;
        struct string_list partial;
        struct pathspec pathspec;
        int refresh_flags = REFRESH_QUIET;
@@ -321,12 +320,11 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
 
        if (interactive) {
                char *old_index_env = NULL;
-               fd = hold_locked_index(&index_lock, 1);
+               hold_locked_index(&index_lock, 1);
 
                refresh_cache_or_die(refresh_flags);
 
-               if (write_cache(fd, active_cache, active_nr) ||
-                   close_lock_file(&index_lock))
+               if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                        die(_("unable to create temporary index"));
 
                old_index_env = getenv(INDEX_ENVIRONMENT);
@@ -360,12 +358,11 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
         * (B) on failure, rollback the real index.
         */
        if (all || (also && pathspec.nr)) {
-               fd = hold_locked_index(&index_lock, 1);
+               hold_locked_index(&index_lock, 1);
                add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
                refresh_cache_or_die(refresh_flags);
                update_main_cache_tree(WRITE_TREE_SILENT);
-               if (write_cache(fd, active_cache, active_nr) ||
-                   close_lock_file(&index_lock))
+               if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                        die(_("unable to write new_index file"));
                commit_style = COMMIT_NORMAL;
                return index_lock.filename;
@@ -381,12 +378,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
         * We still need to refresh the index here.
         */
        if (!only && !pathspec.nr) {
-               fd = hold_locked_index(&index_lock, 1);
+               hold_locked_index(&index_lock, 1);
                refresh_cache_or_die(refresh_flags);
                if (active_cache_changed) {
                        update_main_cache_tree(WRITE_TREE_SILENT);
-                       if (write_cache(fd, active_cache, active_nr) ||
-                           commit_locked_index(&index_lock))
+                       if (write_locked_index(&the_index, &index_lock,
+                                              COMMIT_LOCK))
                                die(_("unable to write new_index file"));
                } else {
                        rollback_lock_file(&index_lock);
@@ -423,8 +420,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
                        die(_("cannot do a partial commit during a cherry-pick."));
        }
 
-       memset(&partial, 0, sizeof(partial));
-       partial.strdup_strings = 1;
+       string_list_init(&partial, 1);
        if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
                exit(1);
 
@@ -432,24 +428,22 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
        if (read_cache() < 0)
                die(_("cannot read the index"));
 
-       fd = hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, 1);
        add_remove_files(&partial);
        refresh_cache(REFRESH_QUIET);
-       if (write_cache(fd, active_cache, active_nr) ||
-           close_lock_file(&index_lock))
+       if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                die(_("unable to write new_index file"));
 
-       fd = hold_lock_file_for_update(&false_lock,
-                                      git_path("next-index-%"PRIuMAX,
-                                               (uintmax_t) getpid()),
-                                      LOCK_DIE_ON_ERROR);
+       hold_lock_file_for_update(&false_lock,
+                                 git_path("next-index-%"PRIuMAX,
+                                          (uintmax_t) getpid()),
+                                 LOCK_DIE_ON_ERROR);
 
        create_base_index(current_head);
        add_remove_files(&partial);
        refresh_cache(REFRESH_QUIET);
 
-       if (write_cache(fd, active_cache, active_nr) ||
-           close_lock_file(&false_lock))
+       if (write_locked_index(&the_index, &false_lock, CLOSE_LOCK))
                die(_("unable to write temporary index file"));
 
        discard_cache();
@@ -707,7 +701,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                char *buffer;
                buffer = strstr(use_message_buffer, "\n\n");
                if (buffer)
-                       strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+                       strbuf_addstr(&sb, buffer + 2);
                hook_arg1 = "commit";
                hook_arg2 = use_message;
        } else if (fixup_message) {
index 24d740c8b1705f9c8993bd808c9a618acecc300c..ee6a3b998f2c22b1d131a180e77079117e598136 100644 (file)
@@ -56,18 +56,9 @@ static int commit_name_cmp(const struct commit_name *cn1,
        return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled);
 }
 
-static inline unsigned int hash_sha1(const unsigned char *sha1)
-{
-       unsigned int hash;
-       memcpy(&hash, sha1, sizeof(hash));
-       return hash;
-}
-
 static inline struct commit_name *find_commit_name(const unsigned char *peeled)
 {
-       struct commit_name key;
-       hashmap_entry_init(&key, hash_sha1(peeled));
-       return hashmap_get(&names, &key, peeled);
+       return hashmap_get_from_hash(&names, sha1hash(peeled), peeled);
 }
 
 static int replace_name(struct commit_name *e,
@@ -114,7 +105,7 @@ static void add_to_known_names(const char *path,
                if (!e) {
                        e = xmalloc(sizeof(struct commit_name));
                        hashcpy(e->peeled, peeled);
-                       hashmap_entry_init(e, hash_sha1(peeled));
+                       hashmap_entry_init(e, sha1hash(peeled));
                        hashmap_add(&names, e);
                        e->path = NULL;
                }
index ce0e019e0caa358e1f5e39e5ed51bec3945197e0..1c4ad6223eb9547666ec21f81f410ef1a459ec92 100644 (file)
@@ -68,9 +68,7 @@ static int diff_tree_stdin(char *line)
        line[len-1] = 0;
        if (get_sha1_hex(line, sha1))
                return -1;
-       obj = lookup_unknown_object(sha1);
-       if (!obj || !obj->parsed)
-               obj = parse_object(sha1);
+       obj = parse_object(sha1);
        if (!obj)
                return -1;
        if (obj->type == OBJ_COMMIT)
index 4135980f20786ef3320e334c4001fd67fc169056..47bd624696d5e94295dda8846f00dcbb80a6ca5a 100644 (file)
@@ -283,18 +283,6 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
        }
 }
 
-static int num_parents(struct commit *commit)
-{
-       struct commit_list *parents;
-       int i;
-
-       for (i = 0, parents = commit->parents;
-            parents;
-            parents = parents->next)
-               i++;
-       return i;
-}
-
 /* See grab_values */
 static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
 {
@@ -315,12 +303,12 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
                }
                if (!strcmp(name, "numparent")) {
                        char *s = xmalloc(40);
-                       v->ul = num_parents(commit);
+                       v->ul = commit_list_count(commit->parents);
                        sprintf(s, "%lu", v->ul);
                        v->s = s;
                }
                else if (!strcmp(name, "parent")) {
-                       int num = num_parents(commit);
+                       int num = commit_list_count(commit->parents);
                        int i;
                        struct commit_list *parents;
                        char *s = xmalloc(41 * num + 1);
index 8aadca160e857be8a87ec1f152e51eb6f51c90b5..d42a27da89d8d5693c6335cf4e2123d41c030580 100644 (file)
@@ -481,11 +481,6 @@ static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, in
        return 0;
 }
 
-static int is_branch(const char *refname)
-{
-       return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
-}
-
 static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct object *obj;
index 8b3bd29dbcf22668040c53fffb6e2414ca090f5d..5568a5bc3b69be79f0d5fa09c694fd976c8319cc 100644 (file)
@@ -362,8 +362,7 @@ static void set_thread_data(struct thread_local *data)
 
 static struct base_data *alloc_base_data(void)
 {
-       struct base_data *base = xmalloc(sizeof(struct base_data));
-       memset(base, 0, sizeof(*base));
+       struct base_data *base = xcalloc(1, sizeof(struct base_data));
        base->ref_last = -1;
        base->ofs_last = -1;
        return base;
@@ -1506,7 +1505,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        const char *curr_index;
        const char *index_name = NULL, *pack_name = NULL;
        const char *keep_name = NULL, *keep_msg = NULL;
-       char *index_name_buf = NULL, *keep_name_buf = NULL;
+       struct strbuf index_name_buf = STRBUF_INIT,
+                     keep_name_buf = STRBUF_INIT;
        struct pack_idx_entry **idx_objects;
        struct pack_idx_option opts;
        unsigned char pack_sha1[20];
@@ -1603,24 +1603,22 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (fix_thin_pack && !from_stdin)
                die(_("--fix-thin cannot be used without --stdin"));
        if (!index_name && pack_name) {
-               int len = strlen(pack_name);
-               if (!has_extension(pack_name, ".pack"))
+               size_t len;
+               if (!strip_suffix(pack_name, ".pack", &len))
                        die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
-               index_name_buf = xmalloc(len);
-               memcpy(index_name_buf, pack_name, len - 5);
-               strcpy(index_name_buf + len - 5, ".idx");
-               index_name = index_name_buf;
+               strbuf_add(&index_name_buf, pack_name, len);
+               strbuf_addstr(&index_name_buf, ".idx");
+               index_name = index_name_buf.buf;
        }
        if (keep_msg && !keep_name && pack_name) {
-               int len = strlen(pack_name);
-               if (!has_extension(pack_name, ".pack"))
+               size_t len;
+               if (!strip_suffix(pack_name, ".pack", &len))
                        die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
-               keep_name_buf = xmalloc(len);
-               memcpy(keep_name_buf, pack_name, len - 5);
-               strcpy(keep_name_buf + len - 5, ".keep");
-               keep_name = keep_name_buf;
+               strbuf_add(&keep_name_buf, pack_name, len);
+               strbuf_addstr(&keep_name_buf, ".idx");
+               keep_name = keep_name_buf.buf;
        }
        if (verify) {
                if (!index_name)
@@ -1668,8 +1666,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        else
                close(input_fd);
        free(objects);
-       free(index_name_buf);
-       free(keep_name_buf);
+       strbuf_release(&index_name_buf);
+       strbuf_release(&keep_name_buf);
        if (pack_name == NULL)
                free((void *) curr_pack);
        if (index_name == NULL)
index 27c1b65db46bb1bead9045b7b6be08a1ee199835..4389722b4b1edffce8cd2a63dae52277e022ac99 100644 (file)
@@ -861,7 +861,7 @@ static void add_branch_description(struct strbuf *buf, const char *branch_name)
        read_branch_desc(&desc, branch_name);
        if (desc.len) {
                strbuf_addch(buf, '\n');
-               strbuf_add(buf, desc.buf, desc.len);
+               strbuf_addbuf(buf, &desc);
                strbuf_addch(buf, '\n');
        }
 }
index 86e9c61277990e2999d678af95824d5aec0b3df0..ce82eb297db3d03394f4e73a9b07574661db7090 100644 (file)
@@ -657,14 +657,12 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                              struct commit_list *remoteheads,
                              struct commit *head, const char *head_arg)
 {
-       int index_fd;
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 
-       index_fd = hold_locked_index(lock, 1);
+       hold_locked_index(lock, 1);
        refresh_cache(REFRESH_QUIET);
        if (active_cache_changed &&
-                       (write_cache(index_fd, active_cache, active_nr) ||
-                        commit_locked_index(lock)))
+           write_locked_index(&the_index, lock, COMMIT_LOCK))
                return error(_("Unable to write index."));
        rollback_lock_file(lock);
 
@@ -672,7 +670,6 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                int clean, x;
                struct commit *result;
                struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-               int index_fd;
                struct commit_list *reversed = NULL;
                struct merge_options o;
                struct commit_list *j;
@@ -700,12 +697,11 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
 
-               index_fd = hold_locked_index(lock, 1);
+               hold_locked_index(lock, 1);
                clean = merge_recursive(&o, head,
                                remoteheads->item, reversed, &result);
                if (active_cache_changed &&
-                               (write_cache(index_fd, active_cache, active_nr) ||
-                                commit_locked_index(lock)))
+                   write_locked_index(&the_index, lock, COMMIT_LOCK))
                        die (_("unable to write %s"), get_index_file());
                rollback_lock_file(lock);
                return clean ? 0 : 1;
@@ -843,16 +839,14 @@ static void prepare_to_commit(struct commit_list *remoteheads)
 static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
 {
        unsigned char result_tree[20], result_commit[20];
-       struct commit_list *parent = xmalloc(sizeof(*parent));
+       struct commit_list *parents, **pptr = &parents;
 
        write_tree_trivial(result_tree);
        printf(_("Wonderful.\n"));
-       parent->item = head;
-       parent->next = xmalloc(sizeof(*parent->next));
-       parent->next->item = remoteheads->item;
-       parent->next->next = NULL;
+       pptr = commit_list_append(head, pptr);
+       pptr = commit_list_append(remoteheads->item, pptr);
        prepare_to_commit(remoteheads);
-       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parent,
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
                        result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        finish(head, remoteheads, result_commit, "In-index merge");
index 180ef99127d47d0e8fe9f12a590fd9ba9e524f08..6ffe540c202f69a41fcf40d443f73073d3ba08bd 100644 (file)
@@ -63,7 +63,7 @@ static struct lock_file lock_file;
 
 int cmd_mv(int argc, const char **argv, const char *prefix)
 {
-       int i, newfd, gitmodules_modified = 0;
+       int i, gitmodules_modified = 0;
        int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
        struct option builtin_mv_options[] = {
                OPT__VERBOSE(&verbose, N_("be verbose")),
@@ -85,7 +85,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        if (--argc < 1)
                usage_with_options(builtin_mv_usage, builtin_mv_options);
 
-       newfd = hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, 1);
        if (read_cache() < 0)
                die(_("index file corrupt"));
 
@@ -276,8 +276,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                stage_updated_gitmodules();
 
        if (active_cache_changed) {
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(&lock_file))
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
 
index 0d7ef847a70581214fb88c3a981581670f7decbe..e7e1c33a7fb1f1eb7828f4d632fb492f128f66fe 100644 (file)
@@ -99,7 +99,7 @@ static struct lock_file lock_file;
 
 int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 {
-       int i, newfd, stage = 0;
+       int i, stage = 0;
        unsigned char sha1[20];
        struct tree_desc t[MAX_UNPACK_TREES];
        struct unpack_trees_options opts;
@@ -149,12 +149,21 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        argc = parse_options(argc, argv, unused_prefix, read_tree_options,
                             read_tree_usage, 0);
 
-       newfd = hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, 1);
 
        prefix_set = opts.prefix ? 1 : 0;
        if (1 < opts.merge + opts.reset + prefix_set)
                die("Which one? -m, --reset, or --prefix?");
 
+       /*
+        * NEEDSWORK
+        *
+        * The old index should be read anyway even if we're going to
+        * destroy all index entries because we still need to preserve
+        * certain information such as index version or split-index
+        * mode.
+        */
+
        if (opts.reset || opts.merge || opts.prefix) {
                if (read_cache_unmerged() && (opts.prefix || opts.merge))
                        die("You need to resolve your current index first");
@@ -231,10 +240,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
         * what came from the tree.
         */
        if (nr_trees == 1 && !opts.prefix)
-               prime_cache_tree(&active_cache_tree, trees[0]);
+               prime_cache_tree(&the_index, trees[0]);
 
-       if (write_cache(newfd, active_cache, active_nr) ||
-           commit_locked_index(&lock_file))
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die("unable to write new index file");
        return 0;
 }
index 18458e81c6351d53afe1a50efff183889e5ab81b..f93ac454b4133f5c1e7cb1675a618b4c0b3174cd 100644 (file)
@@ -438,7 +438,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
        uint32_t mask = 1 << (cmd->index % 32);
        int i;
 
-       trace_printf_key("GIT_TRACE_SHALLOW",
+       trace_printf_key(&trace_shallow,
                         "shallow: update_shallow_ref %s\n", cmd->ref_name);
        for (i = 0; i < si->shallow->nr; i++)
                if (si->used_shallow[i] &&
@@ -1122,7 +1122,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        int advertise_refs = 0;
        int stateless_rpc = 0;
        int i;
-       char *dir = NULL;
+       const char *dir = NULL;
        struct command *commands;
        struct sha1_array shallow = SHA1_ARRAY_INIT;
        struct sha1_array ref = SHA1_ARRAY_INIT;
@@ -1157,7 +1157,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                }
                if (dir)
                        usage(receive_pack_usage);
-               dir = xstrdup(arg);
+               dir = arg;
        }
        if (!dir)
                usage(receive_pack_usage);
index a8efe3da4d7dad3b17582c91671f0f475d54a9ac..9a4640dbf0150bff38efcbb0126abdf1aeae7ed4 100644 (file)
@@ -263,16 +263,17 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                struct string_list_item *item;
                struct branch_info *info;
                enum { REMOTE, MERGE, REBASE } type;
+               size_t key_len;
 
                key += 7;
-               if (ends_with(key, ".remote")) {
-                       name = xstrndup(key, strlen(key) - 7);
+               if (strip_suffix(key, ".remote", &key_len)) {
+                       name = xmemdupz(key, key_len);
                        type = REMOTE;
-               } else if (ends_with(key, ".merge")) {
-                       name = xstrndup(key, strlen(key) - 6);
+               } else if (strip_suffix(key, ".merge", &key_len)) {
+                       name = xmemdupz(key, key_len);
                        type = MERGE;
-               } else if (ends_with(key, ".rebase")) {
-                       name = xstrndup(key, strlen(key) - 7);
+               } else if (strip_suffix(key, ".rebase", &key_len)) {
+                       name = xmemdupz(key, key_len);
                        type = REBASE;
                } else
                        return 0;
@@ -753,7 +754,7 @@ static int remove_branches(struct string_list *branches)
        branch_names = xmalloc(branches->nr * sizeof(*branch_names));
        for (i = 0; i < branches->nr; i++)
                branch_names[i] = branches->items[i].string;
-       result |= repack_without_refs(branch_names, branches->nr);
+       result |= repack_without_refs(branch_names, branches->nr, NULL);
        free(branch_names);
 
        for (i = 0; i < branches->nr; i++) {
@@ -1331,7 +1332,8 @@ static int prune_remote(const char *remote, int dry_run)
                for (i = 0; i < states.stale.nr; i++)
                        delete_refs[i] = states.stale.items[i].util;
                if (!dry_run)
-                       result |= repack_without_refs(delete_refs, states.stale.nr);
+                       result |= repack_without_refs(delete_refs,
+                                                     states.stale.nr, NULL);
                free(delete_refs);
        }
 
index ff2216a7aaefa3b38385301e4e0d8bb8b94e4ea0..a77e743b94036b2d856e6b3f74999170e39b5f17 100644 (file)
@@ -83,16 +83,15 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
        DIR *dir;
        struct dirent *e;
        char *fname;
-       size_t len;
 
        if (!(dir = opendir(packdir)))
                return;
 
        while ((e = readdir(dir)) != NULL) {
-               if (!ends_with(e->d_name, ".pack"))
+               size_t len;
+               if (!strip_suffix(e->d_name, ".pack", &len))
                        continue;
 
-               len = strlen(e->d_name) - strlen(".pack");
                fname = xmemdupz(e->d_name, len);
 
                if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
index 1bb491d3c4f282e7f68d25cae0e00f419df1dd93..294b61b97e20ac9b5684bd6a180da37ebef43db9 100644 (file)
 #include "refs.h"
 #include "parse-options.h"
 #include "run-command.h"
+#include "tag.h"
 
 static const char * const git_replace_usage[] = {
        N_("git replace [-f] <object> <replacement>"),
        N_("git replace [-f] --edit <object>"),
+       N_("git replace [-f] --graft <commit> [<parent>...]"),
        N_("git replace -d <object>..."),
        N_("git replace [--format=<format>] [-l [<pattern>]]"),
        NULL
 };
 
 enum replace_format {
-      REPLACE_FORMAT_SHORT,
-      REPLACE_FORMAT_MEDIUM,
-      REPLACE_FORMAT_LONG
+       REPLACE_FORMAT_SHORT,
+       REPLACE_FORMAT_MEDIUM,
+       REPLACE_FORMAT_LONG
 };
 
 struct show_data {
@@ -188,27 +190,32 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f
 }
 
 /*
- * Write the contents of the object named by "sha1" to the file "filename",
- * pretty-printed for human editing based on its type.
+ * Write the contents of the object named by "sha1" to the file "filename".
+ * If "raw" is true, then the object's raw contents are printed according to
+ * "type". Otherwise, we pretty-print the contents for human editing.
  */
-static void export_object(const unsigned char *sha1, const char *filename)
+static void export_object(const unsigned char *sha1, enum object_type type,
+                         int raw, const char *filename)
 {
-       const char *argv[] = { "--no-replace-objects", "cat-file", "-p", NULL, NULL };
-       struct child_process cmd = { argv };
+       struct child_process cmd = { NULL };
        int fd;
 
        fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
        if (fd < 0)
                die_errno("unable to open %s for writing", filename);
 
-       argv[3] = sha1_to_hex(sha1);
+       argv_array_push(&cmd.args, "--no-replace-objects");
+       argv_array_push(&cmd.args, "cat-file");
+       if (raw)
+               argv_array_push(&cmd.args, typename(type));
+       else
+               argv_array_push(&cmd.args, "-p");
+       argv_array_push(&cmd.args, sha1_to_hex(sha1));
        cmd.git_cmd = 1;
        cmd.out = fd;
 
        if (run_command(&cmd))
                die("cat-file reported failure");
-
-       close(fd);
 }
 
 /*
@@ -217,7 +224,7 @@ static void export_object(const unsigned char *sha1, const char *filename)
  * The sha1 of the written object is returned via sha1.
  */
 static void import_object(unsigned char *sha1, enum object_type type,
-                         const char *filename)
+                         int raw, const char *filename)
 {
        int fd;
 
@@ -225,7 +232,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
        if (fd < 0)
                die_errno("unable to open %s for reading", filename);
 
-       if (type == OBJ_TREE) {
+       if (!raw && type == OBJ_TREE) {
                const char *argv[] = { "mktree", NULL };
                struct child_process cmd = { argv };
                struct strbuf result = STRBUF_INIT;
@@ -265,7 +272,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
         */
 }
 
-static int edit_and_replace(const char *object_ref, int force)
+static int edit_and_replace(const char *object_ref, int force, int raw)
 {
        char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
        enum object_type type;
@@ -281,10 +288,10 @@ static int edit_and_replace(const char *object_ref, int force)
 
        check_ref_valid(old, prev, ref, sizeof(ref), force);
 
-       export_object(old, tmpfile);
+       export_object(old, type, raw, tmpfile);
        if (launch_editor(tmpfile, NULL, NULL) < 0)
                die("editing object file failed");
-       import_object(new, type, tmpfile);
+       import_object(new, type, raw, tmpfile);
 
        free(tmpfile);
 
@@ -294,22 +301,137 @@ static int edit_and_replace(const char *object_ref, int force)
        return replace_object_sha1(object_ref, old, "replacement", new, force);
 }
 
+static void replace_parents(struct strbuf *buf, int argc, const char **argv)
+{
+       struct strbuf new_parents = STRBUF_INIT;
+       const char *parent_start, *parent_end;
+       int i;
+
+       /* find existing parents */
+       parent_start = buf->buf;
+       parent_start += 46; /* "tree " + "hex sha1" + "\n" */
+       parent_end = parent_start;
+
+       while (starts_with(parent_end, "parent "))
+               parent_end += 48; /* "parent " + "hex sha1" + "\n" */
+
+       /* prepare new parents */
+       for (i = 0; i < argc; i++) {
+               unsigned char sha1[20];
+               if (get_sha1(argv[i], sha1) < 0)
+                       die(_("Not a valid object name: '%s'"), argv[i]);
+               lookup_commit_or_die(sha1, argv[i]);
+               strbuf_addf(&new_parents, "parent %s\n", sha1_to_hex(sha1));
+       }
+
+       /* replace existing parents with new ones */
+       strbuf_splice(buf, parent_start - buf->buf, parent_end - parent_start,
+                     new_parents.buf, new_parents.len);
+
+       strbuf_release(&new_parents);
+}
+
+struct check_mergetag_data {
+       int argc;
+       const char **argv;
+};
+
+static void check_one_mergetag(struct commit *commit,
+                              struct commit_extra_header *extra,
+                              void *data)
+{
+       struct check_mergetag_data *mergetag_data = (struct check_mergetag_data *)data;
+       const char *ref = mergetag_data->argv[0];
+       unsigned char tag_sha1[20];
+       struct tag *tag;
+       int i;
+
+       hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_sha1);
+       tag = lookup_tag(tag_sha1);
+       if (!tag)
+               die(_("bad mergetag in commit '%s'"), ref);
+       if (parse_tag_buffer(tag, extra->value, extra->len))
+               die(_("malformed mergetag in commit '%s'"), ref);
+
+       /* iterate over new parents */
+       for (i = 1; i < mergetag_data->argc; i++) {
+               unsigned char sha1[20];
+               if (get_sha1(mergetag_data->argv[i], sha1) < 0)
+                       die(_("Not a valid object name: '%s'"), mergetag_data->argv[i]);
+               if (!hashcmp(tag->tagged->sha1, sha1))
+                       return; /* found */
+       }
+
+       die(_("original commit '%s' contains mergetag '%s' that is discarded; "
+             "use --edit instead of --graft"), ref, sha1_to_hex(tag_sha1));
+}
+
+static void check_mergetags(struct commit *commit, int argc, const char **argv)
+{
+       struct check_mergetag_data mergetag_data;
+
+       mergetag_data.argc = argc;
+       mergetag_data.argv = argv;
+       for_each_mergetag(check_one_mergetag, commit, &mergetag_data);
+}
+
+static int create_graft(int argc, const char **argv, int force)
+{
+       unsigned char old[20], new[20];
+       const char *old_ref = argv[0];
+       struct commit *commit;
+       struct strbuf buf = STRBUF_INIT;
+       const char *buffer;
+       unsigned long size;
+
+       if (get_sha1(old_ref, old) < 0)
+               die(_("Not a valid object name: '%s'"), old_ref);
+       commit = lookup_commit_or_die(old, old_ref);
+
+       buffer = get_commit_buffer(commit, &size);
+       strbuf_add(&buf, buffer, size);
+       unuse_commit_buffer(commit, buffer);
+
+       replace_parents(&buf, argc - 1, &argv[1]);
+
+       if (remove_signature(&buf)) {
+               warning(_("the original commit '%s' has a gpg signature."), old_ref);
+               warning(_("the signature will be removed in the replacement commit!"));
+       }
+
+       check_mergetags(commit, argc, argv);
+
+       if (write_sha1_file(buf.buf, buf.len, commit_type, new))
+               die(_("could not write replacement commit for: '%s'"), old_ref);
+
+       strbuf_release(&buf);
+
+       if (!hashcmp(old, new))
+               return error("new commit is the same as the old one: '%s'", sha1_to_hex(old));
+
+       return replace_object_sha1(old_ref, old, "replacement", new, force);
+}
+
 int cmd_replace(int argc, const char **argv, const char *prefix)
 {
        int force = 0;
+       int raw = 0;
        const char *format = NULL;
        enum {
                MODE_UNSPECIFIED = 0,
                MODE_LIST,
                MODE_DELETE,
                MODE_EDIT,
+               MODE_GRAFT,
                MODE_REPLACE
        } cmdmode = MODE_UNSPECIFIED;
        struct option options[] = {
                OPT_CMDMODE('l', "list", &cmdmode, N_("list replace refs"), MODE_LIST),
                OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
                OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
+               OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT),
                OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
+               OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")),
                OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
                OPT_END()
        };
@@ -325,10 +447,17 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                usage_msg_opt("--format cannot be used when not listing",
                              git_replace_usage, options);
 
-       if (force && cmdmode != MODE_REPLACE && cmdmode != MODE_EDIT)
+       if (force &&
+           cmdmode != MODE_REPLACE &&
+           cmdmode != MODE_EDIT &&
+           cmdmode != MODE_GRAFT)
                usage_msg_opt("-f only makes sense when writing a replacement",
                              git_replace_usage, options);
 
+       if (raw && cmdmode != MODE_EDIT)
+               usage_msg_opt("--raw only makes sense with --edit",
+                             git_replace_usage, options);
+
        switch (cmdmode) {
        case MODE_DELETE:
                if (argc < 1)
@@ -346,7 +475,13 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                if (argc != 1)
                        usage_msg_opt("-e needs exactly one argument",
                                      git_replace_usage, options);
-               return edit_and_replace(argv[0], force);
+               return edit_and_replace(argv[0], force, raw);
+
+       case MODE_GRAFT:
+               if (argc < 1)
+                       usage_msg_opt("-g needs at least one argument",
+                                     git_replace_usage, options);
+               return create_graft(argc, argv, force);
 
        case MODE_LIST:
                if (argc > 1)
index 850d53229a1ac757d294ffca6261c9a28af014df..855d478e3b9d2a815ecdcfbf39c261f98ad0994b 100644 (file)
@@ -84,7 +84,7 @@ static int reset_index(const unsigned char *sha1, int reset_type, int quiet)
 
        if (reset_type == MIXED || reset_type == HARD) {
                tree = parse_tree_indirect(sha1);
-               prime_cache_tree(&active_cache_tree, tree);
+               prime_cache_tree(&the_index, tree);
        }
 
        return 0;
@@ -353,7 +353,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        if (reset_type != SOFT) {
                struct lock_file *lock = xcalloc(1, sizeof(*lock));
-               int newfd = hold_locked_index(lock, 1);
+               hold_locked_index(lock, 1);
                if (reset_type == MIXED) {
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
                        if (read_from_tree(&pathspec, sha1, intent_to_add))
@@ -369,8 +369,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                die(_("Could not reset index file to revision '%s'."), rev);
                }
 
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(lock))
+               if (write_locked_index(&the_index, lock, COMMIT_LOCK))
                        die(_("Could not write new index file."));
        }
 
index 1a6122d3ae29609c30c26959fbb1ff799e13a4b2..d85e08cc9cc22ee307470dbeb12fdd8d1eea655b 100644 (file)
@@ -11,6 +11,7 @@
 #include "parse-options.h"
 #include "diff.h"
 #include "revision.h"
+#include "split-index.h"
 
 #define DO_REVS                1
 #define DO_NOREV       2
@@ -150,6 +151,7 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
                                error("refname '%s' is ambiguous", name);
                                break;
                        }
+                       free(full);
                } else {
                        show_with_type(type, name);
                }
@@ -775,6 +777,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                                : "false");
                                continue;
                        }
+                       if (!strcmp(arg, "--shared-index-path")) {
+                               if (read_cache() < 0)
+                                       die(_("Could not read the index"));
+                               if (the_index.split_index) {
+                                       const unsigned char *sha1 = the_index.split_index->base_sha1;
+                                       puts(git_path("sharedindex.%s", sha1_to_hex(sha1)));
+                               }
+                               continue;
+                       }
                        if (starts_with(arg, "--since=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
index 960634dd0c52f1da689e8a54980e5e81cf2d2f36..bc6490b8bca554c568973997568543e206ffdb3e 100644 (file)
@@ -278,7 +278,7 @@ static struct option builtin_rm_options[] = {
 
 int cmd_rm(int argc, const char **argv, const char *prefix)
 {
-       int i, newfd;
+       int i;
        struct pathspec pathspec;
        char *seen;
 
@@ -293,7 +293,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        if (!index_only)
                setup_work_tree();
 
-       newfd = hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, 1);
 
        if (read_cache() < 0)
                die(_("index file corrupt"));
@@ -427,8 +427,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        }
 
        if (active_cache_changed) {
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(&lock_file))
+               if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
 
index 5fd4e4e48839a9dc2998d3fba0e48008b902b12a..298c95e3f8b03e7f89d7767407ca8cee3eecc591 100644 (file)
@@ -777,6 +777,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                        sprintf(nth_desc, "%s@{%d}", *av, base+i);
                        append_ref(nth_desc, sha1, 1);
                }
+               free(ref);
        }
        else if (all_heads + all_remotes)
                snarf_refs(all_heads, all_remotes);
index ef765563388c384a127d27eaf7ee6fd1badfe84f..19eb7478208d7e9c88ced92f2620572d2b234974 100644 (file)
@@ -32,6 +32,8 @@ static const char * const git_tag_usage[] = {
 #define SORT_MASK       0x7fff
 #define REVERSE_SORT    0x8000
 
+static int tag_sort;
+
 struct tag_filter {
        const char **patterns;
        int lines;
@@ -346,9 +348,51 @@ static const char tag_template_nocleanup[] =
        "Lines starting with '%c' will be kept; you may remove them"
        " yourself if you want to.\n");
 
+/*
+ * Parse a sort string, and return 0 if parsed successfully. Will return
+ * non-zero when the sort string does not parse into a known type. If var is
+ * given, the error message becomes a warning and includes information about
+ * the configuration value.
+ */
+static int parse_sort_string(const char *var, const char *arg, int *sort)
+{
+       int type = 0, flags = 0;
+
+       if (skip_prefix(arg, "-", &arg))
+               flags |= REVERSE_SORT;
+
+       if (skip_prefix(arg, "version:", &arg) || skip_prefix(arg, "v:", &arg))
+               type = VERCMP_SORT;
+       else
+               type = STRCMP_SORT;
+
+       if (strcmp(arg, "refname")) {
+               if (!var)
+                       return error(_("unsupported sort specification '%s'"), arg);
+               else {
+                       warning(_("unsupported sort specification '%s' in variable '%s'"),
+                               var, arg);
+                       return -1;
+               }
+       }
+
+       *sort = (type | flags);
+
+       return 0;
+}
+
 static int git_tag_config(const char *var, const char *value, void *cb)
 {
-       int status = git_gpg_config(var, value, cb);
+       int status;
+
+       if (!strcmp(var, "tag.sort")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               parse_sort_string(var, value, &tag_sort);
+               return 0;
+       }
+
+       status = git_gpg_config(var, value, cb);
        if (status)
                return status;
        if (starts_with(var, "column."))
@@ -522,24 +566,8 @@ static int parse_opt_points_at(const struct option *opt __attribute__((unused)),
 static int parse_opt_sort(const struct option *opt, const char *arg, int unset)
 {
        int *sort = opt->value;
-       int flags = 0;
 
-       if (*arg == '-') {
-               flags |= REVERSE_SORT;
-               arg++;
-       }
-       if (starts_with(arg, "version:")) {
-               *sort = VERCMP_SORT;
-               arg += 8;
-       } else if (starts_with(arg, "v:")) {
-               *sort = VERCMP_SORT;
-               arg += 2;
-       } else
-               *sort = STRCMP_SORT;
-       if (strcmp(arg, "refname"))
-               die(_("unsupported sort specification %s"), arg);
-       *sort |= flags;
-       return 0;
+       return parse_sort_string(NULL, arg, sort);
 }
 
 int cmd_tag(int argc, const char **argv, const char *prefix)
@@ -552,7 +580,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        struct create_tag_options opt;
        char *cleanup_arg = NULL;
        int annotate = 0, force = 0, lines = -1;
-       int cmdmode = 0, sort = 0;
+       int cmdmode = 0;
        const char *msgfile = NULL, *keyid = NULL;
        struct msg_arg msg = { 0, STRBUF_INIT };
        struct commit_list *with_commit = NULL;
@@ -578,7 +606,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                OPT__FORCE(&force, N_("replace the tag if exists")),
                OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
                {
-                       OPTION_CALLBACK, 0, "sort", &sort, N_("type"), N_("sort tags"),
+                       OPTION_CALLBACK, 0, "sort", &tag_sort, N_("type"), N_("sort tags"),
                        PARSE_OPT_NONEG, parse_opt_sort
                },
 
@@ -634,9 +662,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                        copts.padding = 2;
                        run_column_filter(colopts, &copts);
                }
-               if (lines != -1 && sort)
+               if (lines != -1 && tag_sort)
                        die(_("--sort and -n are incompatible"));
-               ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, sort);
+               ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, tag_sort);
                if (column_active(colopts))
                        stop_column_filter();
                return ret;
index ebea285e1b6863bdb8e4b2a30d1487d18ecc5084..e8c7fd4d4957b83de42e7af2ef9f2676bfc294b2 100644 (file)
@@ -13,6 +13,7 @@
 #include "parse-options.h"
 #include "pathspec.h"
 #include "dir.h"
+#include "split-index.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -55,8 +56,9 @@ static int mark_ce_flags(const char *path, int flag, int mark)
                        active_cache[pos]->ce_flags |= flag;
                else
                        active_cache[pos]->ce_flags &= ~flag;
-               cache_tree_invalidate_path(active_cache_tree, path);
-               active_cache_changed = 1;
+               active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+               cache_tree_invalidate_path(&the_index, path);
+               active_cache_changed |= CE_ENTRY_CHANGED;
                return 0;
        }
        return -1;
@@ -267,8 +269,9 @@ static void chmod_path(int flip, const char *path)
        default:
                goto fail;
        }
-       cache_tree_invalidate_path(active_cache_tree, path);
-       active_cache_changed = 1;
+       cache_tree_invalidate_path(&the_index, path);
+       ce->ce_flags |= CE_UPDATE_IN_BASE;
+       active_cache_changed |= CE_ENTRY_CHANGED;
        report("chmod %cx '%s'", flip, path);
        return;
  fail:
@@ -743,6 +746,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        char set_executable_bit = 0;
        struct refresh_params refresh_args = {0, &has_errors};
        int lock_error = 0;
+       int split_index = -1;
        struct lock_file *lock_file;
        struct parse_opt_ctx_t ctx;
        int parseopt_state = PARSE_OPT_UNKNOWN;
@@ -825,6 +829,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                        resolve_undo_clear_callback},
                OPT_INTEGER(0, "index-version", &preferred_index_format,
                        N_("write index in this format")),
+               OPT_BOOL(0, "split-index", &split_index,
+                       N_("enable or disable split index")),
                OPT_END()
        };
 
@@ -892,7 +898,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                            INDEX_FORMAT_LB, INDEX_FORMAT_UB);
 
                if (the_index.version != preferred_index_format)
-                       active_cache_changed = 1;
+                       active_cache_changed |= SOMETHING_CHANGED;
                the_index.version = preferred_index_format;
        }
 
@@ -918,14 +924,27 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                strbuf_release(&buf);
        }
 
+       if (split_index > 0) {
+               init_split_index(&the_index);
+               the_index.cache_changed |= SPLIT_INDEX_ORDERED;
+       } else if (!split_index && the_index.split_index) {
+               /*
+                * can't discard_split_index(&the_index); because that
+                * will destroy split_index->base->cache[], which may
+                * be shared with the_index.cache[]. So yeah we're
+                * leaking a bit here.
+                */
+               the_index.split_index = NULL;
+               the_index.cache_changed |= SOMETHING_CHANGED;
+       }
+
        if (active_cache_changed) {
                if (newfd < 0) {
                        if (refresh_args.flags & REFRESH_QUIET)
                                exit(128);
                        unable_to_lock_index_die(get_index_file(), lock_error);
                }
-               if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_locked_index(lock_file))
+               if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                        die("Unable to write new index file");
        }
 
index 405267f6e2776b3b2bf5c91c1102e090f5ac68f0..3067b11310fb01e9e05a1988898566f211601e07 100644 (file)
@@ -16,6 +16,7 @@ static struct ref_transaction *transaction;
 
 static char line_termination = '\n';
 static int update_flags;
+static struct strbuf err = STRBUF_INIT;
 
 /*
  * Parse one whitespace- or NUL-terminated, possibly C-quoted argument
@@ -197,8 +198,9 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next)
        if (*next != line_termination)
                die("update %s: extra input: %s", refname, next);
 
-       ref_transaction_update(transaction, refname, new_sha1, old_sha1,
-                              update_flags, have_old);
+       if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
+                                  update_flags, have_old, &err))
+               die("%s", err.buf);
 
        update_flags = 0;
        free(refname);
@@ -286,8 +288,9 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
        if (*next != line_termination)
                die("verify %s: extra input: %s", refname, next);
 
-       ref_transaction_update(transaction, refname, new_sha1, old_sha1,
-                              update_flags, have_old);
+       if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
+                                  update_flags, have_old, &err))
+               die("%s", err.buf);
 
        update_flags = 0;
        free(refname);
@@ -359,17 +362,16 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                die("Refusing to perform update with empty message.");
 
        if (read_stdin) {
-               int ret;
                transaction = ref_transaction_begin();
-
                if (delete || no_deref || argc > 0)
                        usage_with_options(git_update_ref_usage, options);
                if (end_null)
                        line_termination = '\0';
                update_refs_stdin();
-               ret = ref_transaction_commit(transaction, msg,
-                                            UPDATE_REFS_DIE_ON_ERR);
-               return ret;
+               if (ref_transaction_commit(transaction, msg, &err))
+                       die("%s", err.buf);
+               ref_transaction_free(transaction);
+               return 0;
        }
 
        if (end_null)
index 66cd2df0f878d362a86e8dbc3fcd49b0c2f64c0c..972579f33c4b0adfb2240ef90b22980dc042c70a 100644 (file)
@@ -27,10 +27,9 @@ static int verify_one_pack(const char *path, unsigned int flags)
         * normalize these forms to "foo.pack" for "index-pack --verify".
         */
        strbuf_addstr(&arg, path);
-       if (has_extension(arg.buf, ".idx"))
-               strbuf_splice(&arg, arg.len - 3, 3, "pack", 4);
-       else if (!has_extension(arg.buf, ".pack"))
-               strbuf_add(&arg, ".pack", 5);
+       if (strbuf_strip_suffix(&arg, ".idx") ||
+           !ends_with(arg.buf, ".pack"))
+               strbuf_addstr(&arg, ".pack");
        argv[2] = arg.buf;
 
        memset(&index_pack, 0, sizeof(index_pack));
index 1222952075fe4c9f3c25373123d8793b7c1692ef..71a21a67fa6bc5ce718dfc6593035f577b2dbc15 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -237,8 +237,6 @@ int create_bundle(struct bundle_header *header, const char *path,
        static struct lock_file lock;
        int bundle_fd = -1;
        int bundle_to_stdout;
-       struct argv_array argv_boundary = ARGV_ARRAY_INIT;
-       struct argv_array argv_pack = ARGV_ARRAY_INIT;
        int i, ref_count = 0;
        struct strbuf buf = STRBUF_INIT;
        struct rev_info revs;
@@ -260,14 +258,12 @@ int create_bundle(struct bundle_header *header, const char *path,
        init_revisions(&revs, NULL);
 
        /* write prerequisites */
-       argv_array_pushl(&argv_boundary,
+       memset(&rls, 0, sizeof(rls));
+       argv_array_pushl(&rls.args,
                         "rev-list", "--boundary", "--pretty=oneline",
                         NULL);
        for (i = 1; i < argc; i++)
-               argv_array_push(&argv_boundary, argv[i]);
-
-       memset(&rls, 0, sizeof(rls));
-       rls.argv = argv_boundary.argv;
+               argv_array_push(&rls.args, argv[i]);
        rls.out = -1;
        rls.git_cmd = 1;
        if (start_command(&rls))
@@ -382,12 +378,11 @@ int create_bundle(struct bundle_header *header, const char *path,
        write_or_die(bundle_fd, "\n", 1);
 
        /* write pack */
-       argv_array_pushl(&argv_pack,
+       memset(&rls, 0, sizeof(rls));
+       argv_array_pushl(&rls.args,
                         "pack-objects", "--all-progress-implied",
                         "--stdout", "--thin", "--delta-base-offset",
                         NULL);
-       memset(&rls, 0, sizeof(rls));
-       rls.argv = argv_pack.argv;
        rls.in = -1;
        rls.out = bundle_fd;
        rls.git_cmd = 1;
index 7fa524a11323621d24c352b0a1108ae953a60cf5..c53f7de2b13acfc1b97c60b04552e57791377442 100644 (file)
@@ -98,7 +98,7 @@ struct cache_tree_sub *cache_tree_sub(struct cache_tree *it, const char *path)
        return find_subtree(it, path, pathlen, 1);
 }
 
-void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
+static int do_invalidate_path(struct cache_tree *it, const char *path)
 {
        /* a/b/c
         * ==> invalidate self
@@ -116,7 +116,7 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
 #endif
 
        if (!it)
-               return;
+               return 0;
        slash = strchrnul(path, '/');
        namelen = slash - path;
        it->entry_count = -1;
@@ -137,14 +137,21 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
                                (it->subtree_nr - pos - 1));
                        it->subtree_nr--;
                }
-               return;
+               return 1;
        }
        down = find_subtree(it, path, namelen, 0);
        if (down)
-               cache_tree_invalidate_path(down->cache_tree, slash + 1);
+               do_invalidate_path(down->cache_tree, slash + 1);
+       return 1;
 }
 
-static int verify_cache(const struct cache_entry * const *cache,
+void cache_tree_invalidate_path(struct index_state *istate, const char *path)
+{
+       if (do_invalidate_path(istate->cache_tree, path))
+               istate->cache_changed |= CACHE_TREE_CHANGED;
+}
+
+static int verify_cache(struct cache_entry **cache,
                        int entries, int flags)
 {
        int i, funny;
@@ -229,7 +236,7 @@ int cache_tree_fully_valid(struct cache_tree *it)
 }
 
 static int update_one(struct cache_tree *it,
-                     const struct cache_entry * const *cache,
+                     struct cache_entry **cache,
                      int entries,
                      const char *base,
                      int baselen,
@@ -391,18 +398,19 @@ static int update_one(struct cache_tree *it,
        return i;
 }
 
-int cache_tree_update(struct cache_tree *it,
-                     const struct cache_entry * const *cache,
-                     int entries,
-                     int flags)
+int cache_tree_update(struct index_state *istate, int flags)
 {
-       int i, skip;
-       i = verify_cache(cache, entries, flags);
+       struct cache_tree *it = istate->cache_tree;
+       struct cache_entry **cache = istate->cache;
+       int entries = istate->cache_nr;
+       int skip, i = verify_cache(cache, entries, flags);
+
        if (i)
                return i;
        i = update_one(it, cache, entries, "", 0, &skip, flags);
        if (i < 0)
                return i;
+       istate->cache_changed |= CACHE_TREE_CHANGED;
        return 0;
 }
 
@@ -590,13 +598,10 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
 
        was_valid = cache_tree_fully_valid(active_cache_tree);
        if (!was_valid) {
-               if (cache_tree_update(active_cache_tree,
-                                     (const struct cache_entry * const *)active_cache,
-                                     active_nr, flags) < 0)
+               if (cache_tree_update(&the_index, flags) < 0)
                        return WRITE_TREE_UNMERGED_INDEX;
                if (0 <= newfd) {
-                       if (!write_cache(newfd, active_cache, active_nr) &&
-                           !commit_lock_file(lock_file))
+                       if (!write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                                newfd = -1;
                }
                /* Not being able to write is fine -- we are only interested
@@ -649,11 +654,12 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
        it->entry_count = cnt;
 }
 
-void prime_cache_tree(struct cache_tree **it, struct tree *tree)
+void prime_cache_tree(struct index_state *istate, struct tree *tree)
 {
-       cache_tree_free(it);
-       *it = cache_tree();
-       prime_cache_tree_rec(*it, tree);
+       cache_tree_free(&istate->cache_tree);
+       istate->cache_tree = cache_tree();
+       prime_cache_tree_rec(istate->cache_tree, tree);
+       istate->cache_changed |= CACHE_TREE_CHANGED;
 }
 
 /*
@@ -692,7 +698,5 @@ int update_main_cache_tree(int flags)
 {
        if (!the_index.cache_tree)
                the_index.cache_tree = cache_tree();
-       return cache_tree_update(the_index.cache_tree,
-                                (const struct cache_entry * const *)the_index.cache,
-                                the_index.cache_nr, flags);
+       return cache_tree_update(&the_index, flags);
 }
index f1923ad1e9ddd3ad9b77a436e66ad083843094a0..b47ccec7f626d7cda34ed17661c1dc3e4f035d6a 100644 (file)
@@ -23,14 +23,14 @@ struct cache_tree {
 
 struct cache_tree *cache_tree(void);
 void cache_tree_free(struct cache_tree **);
-void cache_tree_invalidate_path(struct cache_tree *, const char *);
+void cache_tree_invalidate_path(struct index_state *, const char *);
 struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);
 
 void cache_tree_write(struct strbuf *, struct cache_tree *root);
 struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
 
 int cache_tree_fully_valid(struct cache_tree *);
-int cache_tree_update(struct cache_tree *, const struct cache_entry * const *, int, int);
+int cache_tree_update(struct index_state *, int);
 
 int update_main_cache_tree(int);
 
@@ -46,7 +46,7 @@ int update_main_cache_tree(int);
 #define WRITE_TREE_PREFIX_ERROR (-3)
 
 int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);
-void prime_cache_tree(struct cache_tree **, struct tree *);
+void prime_cache_tree(struct index_state *, struct tree *);
 
 extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
 
diff --git a/cache.h b/cache.h
index 44aa43993537f77bf0866b0da7a581d71779e6aa..fcb511db70f7703f2b29dbc89dcf703065c823fe 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -7,6 +7,7 @@
 #include "advice.h"
 #include "gettext.h"
 #include "convert.h"
+#include "trace.h"
 
 #include SHA1_HEADER
 #ifndef git_SHA_CTX
@@ -150,6 +151,7 @@ struct cache_entry {
        unsigned int ce_mode;
        unsigned int ce_flags;
        unsigned int ce_namelen;
+       unsigned int index;     /* for link extension */
        unsigned char sha1[20];
        char name[FLEX_ARRAY]; /* more */
 };
@@ -160,7 +162,7 @@ struct cache_entry {
 #define CE_STAGESHIFT 12
 
 /*
- * Range 0xFFFF0000 in ce_flags is divided into
+ * Range 0xFFFF0FFF in ce_flags is divided into
  * two parts: in-memory flags and on-disk ones.
  * Flags in CE_EXTENDED_FLAGS will get saved on-disk
  * if you want to save a new flag, add it in
@@ -183,6 +185,9 @@ struct cache_entry {
 /* used to temporarily mark paths matched by pathspecs */
 #define CE_MATCHED           (1 << 26)
 
+#define CE_UPDATE_IN_BASE    (1 << 27)
+#define CE_STRIP_NAME        (1 << 28)
+
 /*
  * Extended on-disk flags
  */
@@ -283,12 +288,22 @@ static inline unsigned int canon_mode(unsigned int mode)
 
 #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
 
+#define SOMETHING_CHANGED      (1 << 0) /* unclassified changes go here */
+#define CE_ENTRY_CHANGED       (1 << 1)
+#define CE_ENTRY_REMOVED       (1 << 2)
+#define CE_ENTRY_ADDED         (1 << 3)
+#define RESOLVE_UNDO_CHANGED   (1 << 4)
+#define CACHE_TREE_CHANGED     (1 << 5)
+#define SPLIT_INDEX_ORDERED    (1 << 6)
+
+struct split_index;
 struct index_state {
        struct cache_entry **cache;
        unsigned int version;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
+       struct split_index *split_index;
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
@@ -317,7 +332,6 @@ extern void free_name_hash(struct index_state *istate);
 #define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
 #define is_cache_unborn() is_index_unborn(&the_index)
 #define read_cache_unmerged() read_index_unmerged(&the_index)
-#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
 #define discard_cache() discard_index(&the_index)
 #define unmerged_cache() unmerged_index(&the_index)
 #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
@@ -472,12 +486,17 @@ extern int daemonize(void);
        } while (0)
 
 /* Initialize and use the cache information */
+struct lock_file;
 extern int read_index(struct index_state *);
 extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
+extern int do_read_index(struct index_state *istate, const char *path,
+                        int must_exist); /* for testting only! */
 extern int read_index_from(struct index_state *, const char *path);
 extern int is_index_unborn(struct index_state *);
 extern int read_index_unmerged(struct index_state *);
-extern int write_index(struct index_state *, int newfd);
+#define COMMIT_LOCK            (1 << 0)
+#define CLOSE_LOCK             (1 << 1)
+extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
 extern int discard_index(struct index_state *);
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
@@ -489,6 +508,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
 #define ADD_CACHE_SKIP_DFCHECK 4       /* Ok to skip DF conflict checks */
 #define ADD_CACHE_JUST_APPEND 8                /* Append only; tree.c::read_tree() */
 #define ADD_CACHE_NEW_ONLY 16          /* Do not replace existing ones */
+#define ADD_CACHE_KEEP_CACHE_TREE 32   /* Do not invalidate cache-tree */
 extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
 extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 extern int remove_index_entry_at(struct index_state *, int pos);
@@ -559,6 +579,8 @@ struct lock_file {
 #define LOCK_DIE_ON_ERROR 1
 #define LOCK_NODEREF 2
 extern int unable_to_lock_error(const char *path, int err);
+extern void unable_to_lock_message(const char *path, int err,
+                                  struct strbuf *buf);
 extern NORETURN void unable_to_lock_index_die(const char *path, int err);
 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
@@ -566,7 +588,6 @@ extern int commit_lock_file(struct lock_file *);
 extern void update_index_if_able(struct index_state *, struct lock_file *);
 
 extern int hold_locked_index(struct lock_file *, int);
-extern int commit_locked_index(struct lock_file *);
 extern void set_alternate_index_output(const char *);
 extern int close_lock_file(struct lock_file *);
 extern void rollback_lock_file(struct lock_file *);
@@ -977,7 +998,7 @@ extern int read_ref(const char *refname, unsigned char *sha1);
  * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
  * give up and return NULL.
  *
- * errno is sometimes set on errors, but not always.
+ * errno is set to something meaningful on error.
  */
 extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
 extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
@@ -1078,6 +1099,7 @@ const char *show_ident_date(const struct ident_split *id, enum date_mode mode);
 extern int ident_cmp(const struct ident_split *, const struct ident_split *);
 
 struct checkout {
+       struct index_state *istate;
        const char *base_dir;
        int base_dir_len;
        unsigned force:1,
@@ -1096,7 +1118,7 @@ struct cache_def {
        int prefix_len_stat_func;
 };
 #define CACHE_DEF_INIT { STRBUF_INIT, 0, 0, 0 }
-static inline void cache_def_free(struct cache_def *cache)
+static inline void cache_def_clear(struct cache_def *cache)
 {
        strbuf_release(&cache->path);
 }
@@ -1380,18 +1402,9 @@ extern void *alloc_commit_node(void);
 extern void *alloc_tag_node(void);
 extern void *alloc_object_node(void);
 extern void alloc_report(void);
+extern unsigned int alloc_commit_index(void);
 
-/* trace.c */
-__attribute__((format (printf, 1, 2)))
-extern void trace_printf(const char *format, ...);
-__attribute__((format (printf, 2, 3)))
-extern void trace_argv_printf(const char **argv, const char *format, ...);
-extern void trace_repo_setup(const char *prefix);
-extern int trace_want(const char *key);
-__attribute__((format (printf, 2, 3)))
-extern void trace_printf_key(const char *key, const char *fmt, ...);
-extern void trace_strbuf(const char *key, const struct strbuf *buf);
-
+/* pkt-line.c */
 void packet_trace_identity(const char *prog);
 
 /* add */
index acb74b55d4ee72ddf626754535a9febeb05ba945..ae7f2b10f4e08a549614346c8262b10503cca953 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -18,19 +18,6 @@ int save_commit_buffer = 1;
 
 const char *commit_type = "commit";
 
-static struct commit *check_commit(struct object *obj,
-                                  const unsigned char *sha1,
-                                  int quiet)
-{
-       if (obj->type != OBJ_COMMIT) {
-               if (!quiet)
-                       error("Object %s is a %s, not a commit",
-                             sha1_to_hex(sha1), typename(obj->type));
-               return NULL;
-       }
-       return (struct commit *) obj;
-}
-
 struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
                                              int quiet)
 {
@@ -38,7 +25,7 @@ struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
 
        if (!obj)
                return NULL;
-       return check_commit(obj, sha1, quiet);
+       return object_as_type(obj, OBJ_COMMIT, quiet);
 }
 
 struct commit *lookup_commit_reference(const unsigned char *sha1)
@@ -61,13 +48,9 @@ struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_n
 struct commit *lookup_commit(const unsigned char *sha1)
 {
        struct object *obj = lookup_object(sha1);
-       if (!obj) {
-               struct commit *c = alloc_commit_node();
-               return create_object(sha1, OBJ_COMMIT, c);
-       }
-       if (!obj->type)
-               obj->type = OBJ_COMMIT;
-       return check_commit(obj, sha1, 0);
+       if (!obj)
+               return create_object(sha1, alloc_commit_node());
+       return object_as_type(obj, OBJ_COMMIT, 0);
 }
 
 struct commit *lookup_commit_reference_by_name(const char *name)
@@ -447,12 +430,7 @@ struct commit_list *copy_commit_list(struct commit_list *list)
        struct commit_list *head = NULL;
        struct commit_list **pp = &head;
        while (list) {
-               struct commit_list *new;
-               new = xmalloc(sizeof(struct commit_list));
-               new->item = list->item;
-               new->next = NULL;
-               *pp = new;
-               pp = &new->next;
+               pp = commit_list_append(list->item, pp);
                list = list->next;
        }
        return head;
@@ -786,45 +764,41 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so
 
 static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
 
-static struct commit *interesting(struct commit_list *list)
+static int queue_has_nonstale(struct prio_queue *queue)
 {
-       while (list) {
-               struct commit *commit = list->item;
-               list = list->next;
-               if (commit->object.flags & STALE)
-                       continue;
-               return commit;
+       int i;
+       for (i = 0; i < queue->nr; i++) {
+               struct commit *commit = queue->array[i].data;
+               if (!(commit->object.flags & STALE))
+                       return 1;
        }
-       return NULL;
+       return 0;
 }
 
 /* all input commits in one and twos[] must have been parsed! */
 static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos)
 {
-       struct commit_list *list = NULL;
+       struct prio_queue queue = { compare_commits_by_commit_date };
        struct commit_list *result = NULL;
        int i;
 
        one->object.flags |= PARENT1;
-       commit_list_insert_by_date(one, &list);
-       if (!n)
-               return list;
+       if (!n) {
+               commit_list_append(one, &result);
+               return result;
+       }
+       prio_queue_put(&queue, one);
+
        for (i = 0; i < n; i++) {
                twos[i]->object.flags |= PARENT2;
-               commit_list_insert_by_date(twos[i], &list);
+               prio_queue_put(&queue, twos[i]);
        }
 
-       while (interesting(list)) {
-               struct commit *commit;
+       while (queue_has_nonstale(&queue)) {
+               struct commit *commit = prio_queue_get(&queue);
                struct commit_list *parents;
-               struct commit_list *next;
                int flags;
 
-               commit = list->item;
-               next = list->next;
-               free(list);
-               list = next;
-
                flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
                if (flags == (PARENT1 | PARENT2)) {
                        if (!(commit->object.flags & RESULT)) {
@@ -843,11 +817,11 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n, struc
                        if (parse_commit(p))
                                return NULL;
                        p->object.flags |= flags;
-                       commit_list_insert_by_date(p, &list);
+                       prio_queue_put(&queue, p);
                }
        }
 
-       free_commit_list(list);
+       clear_prio_queue(&queue);
        return result;
 }
 
@@ -992,12 +966,7 @@ struct commit_list *get_merge_bases_many(struct commit *one,
        }
 
        /* There are more than one */
-       cnt = 0;
-       list = result;
-       while (list) {
-               list = list->next;
-               cnt++;
-       }
+       cnt = commit_list_count(result);
        rslt = xcalloc(cnt, sizeof(*rslt));
        for (list = result, i = 0; list; list = list->next)
                rslt[i++] = list->item;
@@ -1177,6 +1146,40 @@ int parse_signed_commit(const struct commit *commit,
        return saw_signature;
 }
 
+int remove_signature(struct strbuf *buf)
+{
+       const char *line = buf->buf;
+       const char *tail = buf->buf + buf->len;
+       int in_signature = 0;
+       const char *sig_start = NULL;
+       const char *sig_end = NULL;
+
+       while (line < tail) {
+               const char *next = memchr(line, '\n', tail - line);
+               next = next ? next + 1 : tail;
+
+               if (in_signature && line[0] == ' ')
+                       sig_end = next;
+               else if (starts_with(line, gpg_sig_header) &&
+                        line[gpg_sig_header_len] == ' ') {
+                       sig_start = line;
+                       sig_end = next;
+                       in_signature = 1;
+               } else {
+                       if (*line == '\n')
+                               /* dump the whole remainder of the buffer */
+                               next = tail;
+                       in_signature = 0;
+               }
+               line = next;
+       }
+
+       if (sig_start)
+               strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start);
+
+       return sig_start != NULL;
+}
+
 static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
 {
        struct merge_remote_desc *desc;
@@ -1315,6 +1318,19 @@ struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
        return extra;
 }
 
+void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data)
+{
+       struct commit_extra_header *extra, *to_free;
+
+       to_free = read_commit_extra_headers(commit, NULL);
+       for (extra = to_free; extra; extra = extra->next) {
+               if (strcmp(extra->key, "mergetag"))
+                       continue; /* not a merge tag */
+               fn(commit, extra, data);
+       }
+       free_commit_extra_headers(to_free);
+}
+
 static inline int standard_header_field(const char *field, size_t len)
 {
        return ((len == 4 && !memcmp(field, "tree ", 5)) ||
index 2e1492a6e4d1c5fc5bea3a48180f128601c3461e..a8cbf52f15ff01778ba61a5a888263d424b2d8d2 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -271,6 +271,7 @@ extern void assign_shallow_commits_to_refs(struct shallow_info *info,
                                           int *ref_status);
 extern int delayed_reachability_test(struct shallow_info *si, int c);
 extern void prune_shallow(int show_only);
+extern struct trace_key trace_shallow;
 
 int is_descendant_of(struct commit *, struct commit_list *);
 int in_merge_bases(struct commit *, struct commit *);
@@ -312,6 +313,11 @@ extern struct commit_extra_header *read_commit_extra_headers(struct commit *, co
 
 extern void free_commit_extra_headers(struct commit_extra_header *extra);
 
+typedef void (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
+                                void *cb_data);
+
+extern void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data);
+
 struct merge_remote_desc {
        struct object *obj; /* the named object, could be a tag */
        const char *name;
@@ -327,6 +333,8 @@ struct commit *get_merge_parent(const char *name);
 
 extern int parse_signed_commit(const struct commit *commit,
                               struct strbuf *message, struct strbuf *signature);
+extern int remove_signature(struct strbuf *buf);
+
 extern void print_commit_list(struct commit_list *list,
                              const char *format_cur,
                              const char *format_last);
index df0fa03194aa696b91132094dd32abb54fdf7cd4..c5c37e53ce0fc534b1739a75c4abde0c67027d14 100644 (file)
@@ -1310,8 +1310,7 @@ static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
        else
                ai->ai_canonname = NULL;
 
-       sin = xmalloc(ai->ai_addrlen);
-       memset(sin, 0, ai->ai_addrlen);
+       sin = xcalloc(1, ai->ai_addrlen);
        sin->sin_family = AF_INET;
        /* Note: getaddrinfo is supposed to allow service to be a string,
         * which should be looked up using getservbyname. This is
index 3851857c2d8a1197e97b6a0baeb5601163de0733..df0e3203abed06374a789be191ff130e3ba728b6 100644 (file)
@@ -35,6 +35,9 @@ typedef int socklen_t;
 #ifndef EWOULDBLOCK
 #define EWOULDBLOCK EAGAIN
 #endif
+#ifndef ELOOP
+#define ELOOP EMLINK
+#endif
 #define SHUT_WR SD_SEND
 
 #define SIGHUP 1
index ba882a1f51de347dc52fa8c52623dbc376f36d80..058505cb8d8d8bb531527b620125b0204732458a 100644 (file)
--- a/config.c
+++ b/config.c
@@ -817,14 +817,12 @@ static int git_default_core_config(const char *var, const char *value)
                return git_config_string(&editor_program, var, value);
 
        if (!strcmp(var, "core.commentchar")) {
-               const char *comment;
-               int ret = git_config_string(&comment, var, value);
-               if (ret)
-                       return ret;
-               else if (!strcasecmp(comment, "auto"))
+               if (!value)
+                       return config_error_nonbool(var);
+               else if (!strcasecmp(value, "auto"))
                        auto_comment_line_char = 1;
-               else if (comment[0] && !comment[1]) {
-                       comment_line_char = comment[0];
+               else if (value[0] && !value[1]) {
+                       comment_line_char = value[0];
                        auto_comment_line_char = 0;
                } else
                        return error("core.commentChar should only be one character");
@@ -1636,8 +1634,8 @@ int git_config_set_multivar_in_file(const char *config_filename,
                        MAP_PRIVATE, in_fd, 0);
                close(in_fd);
 
-               if (fchmod(fd, st.st_mode & 07777) < 0) {
-                       error("fchmod on %s failed: %s",
+               if (chmod(lock->filename, st.st_mode & 07777) < 0) {
+                       error("chmod on %s failed: %s",
                                lock->filename, strerror(errno));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
@@ -1815,8 +1813,8 @@ int git_config_rename_section_in_file(const char *config_filename,
 
        fstat(fileno(config_file), &st);
 
-       if (fchmod(out_fd, st.st_mode & 07777) < 0) {
-               ret = error("fchmod on %s failed: %s",
+       if (chmod(lock->filename, st.st_mode & 07777) < 0) {
+               ret = error("chmod on %s failed: %s",
                                lock->filename, strerror(errno));
                goto out;
        }
index 462988e8ab44f964e093707c9838ee84a7b86e11..15ee15e98c2cb232e1e42315dc44f7efd87b4f1c 100644 (file)
@@ -34,6 +34,7 @@ ifeq ($(uname_S),Linux)
        HAVE_PATHS_H = YesPlease
        LIBC_CONTAINS_LIBINTL = YesPlease
        HAVE_DEV_TTY = YesPlease
+       HAVE_CLOCK_GETTIME = YesPlease
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
        HAVE_ALLOCA_H = YesPlease
index 37ff018f13f6f8f3137a9aec973b85848a8138eb..5047402a1aade7a443f55999550ae4542189ef01 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -64,9 +64,7 @@ static void parse_one_symref_info(struct string_list *symref, const char *val, i
        if (!len)
                return; /* just "symref" */
        /* e.g. "symref=HEAD:refs/heads/master" */
-       sym = xmalloc(len + 1);
-       memcpy(sym, val, len);
-       sym[len] = '\0';
+       sym = xmemdupz(val, len);
        target = strchr(sym, ':');
        if (!target)
                /* just "symref=something" */
index be0253e21bec3617ad3a7f1b0afc7d2049efc4fd..dae9c9972eb3485bd20494efd78d09f1189667cb 100644 (file)
@@ -31,6 +31,7 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
        unsigned char sha1[20];
        int err = 0, ac = 0;
        struct packed_git *new_pack = NULL;
+       size_t base_len;
 
        if (fn(cb_data, sha1))
                return err;
@@ -38,10 +39,9 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
        if (transport && transport->smart_options &&
            transport->smart_options->self_contained_and_connected &&
            transport->pack_lockfile &&
-           ends_with(transport->pack_lockfile, ".keep")) {
+           strip_suffix(transport->pack_lockfile, ".keep", &base_len)) {
                struct strbuf idx_file = STRBUF_INIT;
-               strbuf_addstr(&idx_file, transport->pack_lockfile);
-               strbuf_setlen(&idx_file, idx_file.len - 5); /* ".keep" */
+               strbuf_add(&idx_file, transport->pack_lockfile, base_len);
                strbuf_addstr(&idx_file, ".idx");
                new_pack = add_packed_git(idx_file.buf, idx_file.len, 1);
                strbuf_release(&idx_file);
diff --git a/contrib/convert-grafts-to-replace-refs.sh b/contrib/convert-grafts-to-replace-refs.sh
new file mode 100755 (executable)
index 0000000..0cbc917
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# You should execute this script in the repository where you
+# want to convert grafts to replace refs.
+
+GRAFTS_FILE="${GIT_DIR:-.git}/info/grafts"
+
+. $(git --exec-path)/git-sh-setup
+
+test -f "$GRAFTS_FILE" || die "Could not find graft file: '$GRAFTS_FILE'"
+
+grep '^[^# ]' "$GRAFTS_FILE" |
+while read definition
+do
+       if test -n "$definition"
+       then
+               echo "Converting: $definition"
+               git replace --graft $definition ||
+                       die "Conversion failed for: $definition"
+       fi
+done
+
+mv "$GRAFTS_FILE" "$GRAFTS_FILE.bak" ||
+       die "Could not rename '$GRAFTS_FILE' to '$GRAFTS_FILE.bak'"
+
+echo "Success!"
+echo "All the grafts in '$GRAFTS_FILE' have been converted to replace refs!"
+echo "The grafts file '$GRAFTS_FILE' has been renamed: '$GRAFTS_FILE.bak'"
index d888d4516114c719e46ab7f2aeb24e63618b7fac..d9a0ce2c6337b906617306f466139b5f486a753d 100644 (file)
@@ -18,6 +18,11 @@ RM       ?= rm -f
 ASCIIDOC = asciidoc
 XMLTO    = xmlto
 
+ifndef SHELL_PATH
+       SHELL_PATH = /bin/sh
+endif
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
 ASCIIDOC_CONF = ../../Documentation/asciidoc.conf
 MANPAGE_XSL   = ../../Documentation/manpage-normal.xsl
 
@@ -32,7 +37,8 @@ GIT_SUBTREE_HTML := git-subtree.html
 all: $(GIT_SUBTREE)
 
 $(GIT_SUBTREE): $(GIT_SUBTREE_SH)
-       cp $< $@ && chmod +x $@
+       sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' $< >$@
+       chmod +x $@
 
 doc: $(GIT_SUBTREE_DOC) $(GIT_SUBTREE_HTML)
 
index 1eb663172338ae24ef9229594f893e80c2e52f96..e6b51ed9981f2e4084504a2a092a3f28fc7d4c8a 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -778,7 +778,6 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
                logerror("unable to fork");
        else
                add_child(&cld, addr, addrlen);
-       close(incoming);
 }
 
 static void child_handler(int signo)
index 7cb5d29a89a43ecf7e433db513b66184df3accb8..b2aac90c26296eacdd6c2cdb68a15f3744c595e9 100644 (file)
@@ -8,10 +8,7 @@
 
 static unsigned int hash_obj(const struct object *obj, unsigned int n)
 {
-       unsigned int hash;
-
-       memcpy(&hash, obj->sha1, sizeof(unsigned int));
-       return hash % n;
+       return sha1hash(obj->sha1) % n;
 }
 
 static void *insert_decoration(struct decoration *n, const struct object *base, void *decoration)
diff --git a/diff.c b/diff.c
index 06bdfb8ae5acab2c6090ca4154a26fca6da8b74c..867f034b8ffc052d28f28a86fd9f52a5aa5b2c82 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -525,9 +525,9 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
        ep += 2; /* skip over @@ */
 
        /* The hunk header in fraginfo color */
-       strbuf_add(&msgbuf, frag, strlen(frag));
+       strbuf_addstr(&msgbuf, frag);
        strbuf_add(&msgbuf, line, ep - line);
-       strbuf_add(&msgbuf, reset, strlen(reset));
+       strbuf_addstr(&msgbuf, reset);
 
        /*
         * trailing "\r\n"
@@ -541,15 +541,15 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
                if (*ep != ' ' && *ep != '\t')
                        break;
        if (ep != cp) {
-               strbuf_add(&msgbuf, plain, strlen(plain));
+               strbuf_addstr(&msgbuf, plain);
                strbuf_add(&msgbuf, cp, ep - cp);
-               strbuf_add(&msgbuf, reset, strlen(reset));
+               strbuf_addstr(&msgbuf, reset);
        }
 
        if (ep < line + len) {
-               strbuf_add(&msgbuf, func, strlen(func));
+               strbuf_addstr(&msgbuf, func);
                strbuf_add(&msgbuf, ep, line + len - ep);
-               strbuf_add(&msgbuf, reset, strlen(reset));
+               strbuf_addstr(&msgbuf, reset);
        }
 
        strbuf_add(&msgbuf, line + len, org_len - len);
index 749a35d2c2ab3271c6503a19271a5be6a6e97b4d..2e44a3745939bb75841730ba0cff78ea872df8d9 100644 (file)
@@ -242,14 +242,12 @@ struct file_similarity {
 
 static unsigned int hash_filespec(struct diff_filespec *filespec)
 {
-       unsigned int hash;
        if (!filespec->sha1_valid) {
                if (diff_populate_filespec(filespec, 0))
                        return 0;
                hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1);
        }
-       memcpy(&hash, filespec->sha1, sizeof(hash));
-       return hash;
+       return sha1hash(filespec->sha1);
 }
 
 static int find_identical_files(struct hashmap *srcs,
@@ -259,15 +257,14 @@ static int find_identical_files(struct hashmap *srcs,
        int renames = 0;
 
        struct diff_filespec *target = rename_dst[dst_index].two;
-       struct file_similarity *p, *best, dst;
+       struct file_similarity *p, *best = NULL;
        int i = 100, best_score = -1;
 
        /*
         * Find the best source match for specified destination.
         */
-       best = NULL;
-       hashmap_entry_init(&dst, hash_filespec(target));
-       for (p = hashmap_get(srcs, &dst, NULL); p; p = hashmap_get_next(srcs, p)) {
+       p = hashmap_get_from_hash(srcs, hash_filespec(target), NULL);
+       for (; p; p = hashmap_get_next(srcs, p)) {
                int score;
                struct diff_filespec *source = p->filespec;
 
diff --git a/dir.c b/dir.c
index e65888dfad187960bf77904708e60f532ef19e1a..fcb68729b1f559008e1797ff50f38acf81268d3d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -557,8 +557,7 @@ int add_excludes_from_file_to_list(const char *fname,
                        buf = xrealloc(buf, size+1);
                        buf[size++] = '\n';
                }
-       }
-       else {
+       } else {
                size = xsize_t(st.st_size);
                if (size == 0) {
                        close(fd);
@@ -793,17 +792,19 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
 
        group = &dir->exclude_list_group[EXC_DIRS];
 
-       /* Pop the exclude lists from the EXCL_DIRS exclude_list_group
+       /*
+        * Pop the exclude lists from the EXCL_DIRS exclude_list_group
         * which originate from directories not in the prefix of the
-        * path being checked. */
+        * path being checked.
+        */
        while ((stk = dir->exclude_stack) != NULL) {
                if (stk->baselen <= baselen &&
-                   !strncmp(dir->basebuf, base, stk->baselen))
+                   !strncmp(dir->basebuf.buf, base, stk->baselen))
                        break;
                el = &group->el[dir->exclude_stack->exclude_ix];
                dir->exclude_stack = stk->prev;
                dir->exclude = NULL;
-               free((char *)el->src); /* see strdup() below */
+               free((char *)el->src); /* see strbuf_detach() below */
                clear_exclude_list(el);
                free(stk);
                group->nr--;
@@ -813,8 +814,17 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
        if (dir->exclude)
                return;
 
+       /*
+        * Lazy initialization. All call sites currently just
+        * memset(dir, 0, sizeof(*dir)) before use. Changing all of
+        * them seems lots of work for little benefit.
+        */
+       if (!dir->basebuf.buf)
+               strbuf_init(&dir->basebuf, PATH_MAX);
+
        /* Read from the parent directories and push them down. */
        current = stk ? stk->baselen : -1;
+       strbuf_setlen(&dir->basebuf, current < 0 ? 0 : current);
        while (current < baselen) {
                struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
                const char *cp;
@@ -822,8 +832,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
                if (current < 0) {
                        cp = base;
                        current = 0;
-               }
-               else {
+               } else {
                        cp = strchr(base + current + 1, '/');
                        if (!cp)
                                die("oops in prep_exclude");
@@ -833,48 +842,47 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
                stk->baselen = cp - base;
                stk->exclude_ix = group->nr;
                el = add_exclude_list(dir, EXC_DIRS, NULL);
-               memcpy(dir->basebuf + current, base + current,
-                      stk->baselen - current);
+               strbuf_add(&dir->basebuf, base + current, stk->baselen - current);
+               assert(stk->baselen == dir->basebuf.len);
 
                /* Abort if the directory is excluded */
                if (stk->baselen) {
                        int dt = DT_DIR;
-                       dir->basebuf[stk->baselen - 1] = 0;
+                       dir->basebuf.buf[stk->baselen - 1] = 0;
                        dir->exclude = last_exclude_matching_from_lists(dir,
-                               dir->basebuf, stk->baselen - 1,
-                               dir->basebuf + current, &dt);
-                       dir->basebuf[stk->baselen - 1] = '/';
+                               dir->basebuf.buf, stk->baselen - 1,
+                               dir->basebuf.buf + current, &dt);
+                       dir->basebuf.buf[stk->baselen - 1] = '/';
                        if (dir->exclude &&
                            dir->exclude->flags & EXC_FLAG_NEGATIVE)
                                dir->exclude = NULL;
                        if (dir->exclude) {
-                               dir->basebuf[stk->baselen] = 0;
                                dir->exclude_stack = stk;
                                return;
                        }
                }
 
-               /* Try to read per-directory file unless path is too long */
-               if (dir->exclude_per_dir &&
-                   stk->baselen + strlen(dir->exclude_per_dir) < PATH_MAX) {
-                       strcpy(dir->basebuf + stk->baselen,
-                                       dir->exclude_per_dir);
+               /* Try to read per-directory file */
+               if (dir->exclude_per_dir) {
                        /*
                         * dir->basebuf gets reused by the traversal, but we
                         * need fname to remain unchanged to ensure the src
                         * member of each struct exclude correctly
                         * back-references its source file.  Other invocations
                         * of add_exclude_list provide stable strings, so we
-                        * strdup() and free() here in the caller.
+                        * strbuf_detach() and free() here in the caller.
                         */
-                       el->src = strdup(dir->basebuf);
-                       add_excludes_from_file_to_list(dir->basebuf,
-                                       dir->basebuf, stk->baselen, el, 1);
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addbuf(&sb, &dir->basebuf);
+                       strbuf_addstr(&sb, dir->exclude_per_dir);
+                       el->src = strbuf_detach(&sb, NULL);
+                       add_excludes_from_file_to_list(el->src, el->src,
+                                                      stk->baselen, el, 1);
                }
                dir->exclude_stack = stk;
                current = stk->baselen;
        }
-       dir->basebuf[baselen] = '\0';
+       strbuf_setlen(&dir->basebuf, baselen);
 }
 
 /*
@@ -1671,4 +1679,5 @@ void clear_directory(struct dir_struct *dir)
                free(stk);
                stk = prev;
        }
+       strbuf_release(&dir->basebuf);
 }
diff --git a/dir.h b/dir.h
index 55e53456afab4c9fb8441144ac7393458b553006..6c45e9d4b9a9055539e443ba30166b0b5384bac5 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -15,6 +15,27 @@ struct dir_entry {
 #define EXC_FLAG_MUSTBEDIR 8
 #define EXC_FLAG_NEGATIVE 16
 
+struct exclude {
+       /*
+        * This allows callers of last_exclude_matching() etc.
+        * to determine the origin of the matching pattern.
+        */
+       struct exclude_list *el;
+
+       const char *pattern;
+       int patternlen;
+       int nowildcardlen;
+       const char *base;
+       int baselen;
+       int flags;
+
+       /*
+        * Counting starts from 1 for line numbers in ignore files,
+        * and from -1 decrementing for patterns from CLI args.
+        */
+       int srcpos;
+};
+
 /*
  * Each excludes file will be parsed into a fresh exclude_list which
  * is appended to the relevant exclude_list_group (either EXC_DIRS or
@@ -32,26 +53,7 @@ struct exclude_list {
        /* origin of list, e.g. path to filename, or descriptive string */
        const char *src;
 
-       struct exclude {
-               /*
-                * This allows callers of last_exclude_matching() etc.
-                * to determine the origin of the matching pattern.
-                */
-               struct exclude_list *el;
-
-               const char *pattern;
-               int patternlen;
-               int nowildcardlen;
-               const char *base;
-               int baselen;
-               int flags;
-
-               /*
-                * Counting starts from 1 for line numbers in ignore files,
-                * and from -1 decrementing for patterns from CLI args.
-                */
-               int srcpos;
-       } **excludes;
+       struct exclude **excludes;
 };
 
 /*
@@ -117,7 +119,7 @@ struct dir_struct {
         */
        struct exclude_stack *exclude_stack;
        struct exclude *exclude;
-       char basebuf[PATH_MAX];
+       struct strbuf basebuf;
 };
 
 /*
diff --git a/entry.c b/entry.c
index 77c688262477e783b4bcbc237ef281eecab0661d..1eda8e94714bbd6020d61b4b627f721b1042673c 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -210,9 +210,12 @@ static int write_entry(struct cache_entry *ce,
 
 finish:
        if (state->refresh_cache) {
+               assert(state->istate);
                if (!fstat_done)
                        lstat(ce->name, &st);
                fill_stat_cache_info(ce, &st);
+               ce->ce_flags |= CE_UPDATE_IN_BASE;
+               state->istate->cache_changed |= CE_ENTRY_CHANGED;
        }
        return 0;
 }
index f7f700ef516eea2fe908f66b1518b5e3a1d9fd33..1c2d7afd4cb9b70a324d355c3d69b732b181012b 100644 (file)
@@ -110,9 +110,9 @@ int ewah_serialize(struct ewah_bitmap *self, int fd)
        return ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);
 }
 
-int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len)
+int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
 {
-       uint8_t *ptr = map;
+       const uint8_t *ptr = map;
        size_t i;
 
        self->bit_size = get_be32(ptr);
index 43adeb5c6893381918ea948efa70b4756f831583..f6ad190a038a55e39a0b8b135c995413192f80e3 100644 (file)
@@ -99,8 +99,7 @@ int ewah_serialize(struct ewah_bitmap *self, int fd);
 int ewah_serialize_native(struct ewah_bitmap *self, int fd);
 
 int ewah_deserialize(struct ewah_bitmap *self, int fd);
-int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len);
-int ewah_read_mmap_native(struct ewah_bitmap *self, void *map, size_t len);
+int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len);
 
 uint32_t ewah_checksum(struct ewah_bitmap *self);
 
index fa635f75c328354f7a4b44ca1e22461b1eb16de1..d73f58cbe3fe4522fa24d47f9086d927b3296c8b 100644 (file)
@@ -2324,7 +2324,7 @@ static void file_change_m(const char *p, struct branch *b)
        }
 
        /* Git does not track empty, non-toplevel directories. */
-       if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) {
+       if (S_ISDIR(mode) && !hashcmp(sha1, EMPTY_TREE_SHA1_BIN) && *p) {
                tree_content_remove(&b->branch_tree, p, NULL, 0);
                return;
        }
diff --git a/fsck.c b/fsck.c
index a4e8593e78c85c244d2dc8bac0cca838c14bbd4a..56156fff44a3556e2fa0b691ab103612a4678a3e 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -281,7 +281,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
 {
        unsigned char tree_sha1[20], sha1[20];
        struct commit_graft *graft;
-       int parents = 0;
+       unsigned parent_count, parent_line_count = 0;
        int err;
 
        if (!skip_prefix(buffer, "tree ", &buffer))
@@ -293,27 +293,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
                if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n')
                        return error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1");
                buffer += 41;
-               parents++;
+               parent_line_count++;
        }
        graft = lookup_commit_graft(commit->object.sha1);
+       parent_count = commit_list_count(commit->parents);
        if (graft) {
-               struct commit_list *p = commit->parents;
-               parents = 0;
-               while (p) {
-                       p = p->next;
-                       parents++;
-               }
-               if (graft->nr_parent == -1 && !parents)
+               if (graft->nr_parent == -1 && !parent_count)
                        ; /* shallow commit */
-               else if (graft->nr_parent != parents)
+               else if (graft->nr_parent != parent_count)
                        return error_func(&commit->object, FSCK_ERROR, "graft objects missing");
        } else {
-               struct commit_list *p = commit->parents;
-               while (p && parents) {
-                       p = p->next;
-                       parents--;
-               }
-               if (p || parents)
+               if (parent_count != parent_line_count)
                        return error_func(&commit->object, FSCK_ERROR, "parent objects missing");
        }
        if (!skip_prefix(buffer, "author ", &buffer))
index 9de31807108322aac327f66822d4929974d7ffd5..f587749b7cf6a74be376792eb0ad70b7e5e4f597 100644 (file)
@@ -291,10 +291,12 @@ extern char *gitbasename(char *);
 #else
 #define NORETURN
 #define NORETURN_PTR
+#ifndef __GNUC__
 #ifndef __attribute__
 #define __attribute__(x)
 #endif
 #endif
+#endif
 
 /* The sentinel attribute is valid from gcc version 4.0 */
 #if defined(__GNUC__) && (__GNUC__ >= 4)
@@ -347,7 +349,6 @@ extern void set_error_routine(void (*routine)(const char *err, va_list params));
 extern void set_die_is_recursing_routine(int (*routine)(void));
 
 extern int starts_with(const char *str, const char *prefix);
-extern int ends_with(const char *str, const char *suffix);
 
 /*
  * If the string "str" begins with the string found in "prefix", return 1.
@@ -377,6 +378,39 @@ static inline int skip_prefix(const char *str, const char *prefix,
        return 0;
 }
 
+/*
+ * If buf ends with suffix, return 1 and subtract the length of the suffix
+ * from *len. Otherwise, return 0 and leave *len untouched.
+ */
+static inline int strip_suffix_mem(const char *buf, size_t *len,
+                                  const char *suffix)
+{
+       size_t suflen = strlen(suffix);
+       if (*len < suflen || memcmp(buf + (*len - suflen), suffix, suflen))
+               return 0;
+       *len -= suflen;
+       return 1;
+}
+
+/*
+ * If str ends with suffix, return 1 and set *len to the size of the string
+ * without the suffix. Otherwise, return 0 and set *len to the size of the
+ * string.
+ *
+ * Note that we do _not_ NUL-terminate str to the new length.
+ */
+static inline int strip_suffix(const char *str, const char *suffix, size_t *len)
+{
+       *len = strlen(str);
+       return strip_suffix_mem(str, len, suffix);
+}
+
+static inline int ends_with(const char *str, const char *suffix)
+{
+       size_t len;
+       return strip_suffix(str, suffix, &len);
+}
+
 #if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
 
 #ifndef PROT_READ
@@ -581,13 +615,6 @@ static inline size_t xsize_t(off_t len)
        return (size_t)len;
 }
 
-static inline int has_extension(const char *filename, const char *ext)
-{
-       size_t len = strlen(filename);
-       size_t extlen = strlen(ext);
-       return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
-}
-
 /* in ctype.c, for kwset users */
 extern const char tolower_trans_tbl[256];
 
@@ -704,17 +731,6 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 #endif
 #endif
 
-#if defined(__GNUC__) && defined(__x86_64__)
-#include <emmintrin.h>
-/*
- * This is the system memory page size; it's used so that we can read
- * outside the bounds of an allocation without segfaulting.
- */
-#ifndef PAGE_SIZE
-#define PAGE_SIZE 4096
-#endif
-#endif
-
 #ifdef UNRELIABLE_FSTAT
 #define fstat_is_reliable() 0
 #else
@@ -734,6 +750,10 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 #endif
 #endif
 
+#if defined(__GNUC__) || (_MSC_VER >= 1400)
+#define HAVE_VARIADIC_MACROS 1
+#endif
+
 /*
  * Preserves errno, prints a message, but gives no warning for ENOENT.
  * Always returns the return value of unlink(2).
index 86d6994619151fb63327848e6763b07971017ad7..e6e99f5bb5102d394a25f156dbcba956f246054b 100755 (executable)
@@ -332,7 +332,13 @@ while read commit parents; do
        parentstr=
        for parent in $parents; do
                for reparent in $(map "$parent"); do
-                       parentstr="$parentstr -p $reparent"
+                       case "$parentstr " in
+                       *" -p $reparent "*)
+                               ;;
+                       *)
+                               parentstr="$parentstr -p $reparent"
+                               ;;
+                       esac
                done
        done
        if [ "$filter_parent" ]; then
index ca20e1e66fbda7c27a8a4cfff03ecf02841e1002..f9237323331e992a58cee0af79090a7bbeec434c 100644 (file)
@@ -29,7 +29,13 @@ skip)
        ;;
 esac
 
-test -n "$rebase_root" && root_flag=--root
+if test -z "$rebase_root"
+       # this is now equivalent to ! -z "$upstream"
+then
+       revisions=$upstream...$orig_head
+else
+       revisions=$onto...$orig_head
+fi
 
 ret=0
 if test -n "$keep_empty"
@@ -38,14 +44,17 @@ then
        # empty commits and even if it didn't the format doesn't really lend
        # itself well to recording empty patches.  fortunately, cherry-pick
        # makes this easy
-       git cherry-pick ${gpg_sign_opt:+"$gpg_sign_opt"} --allow-empty "$revisions"
+       git cherry-pick ${gpg_sign_opt:+"$gpg_sign_opt"} --allow-empty \
+               --right-only "$revisions" \
+               ${restrict_revision+^$restrict_revision}
        ret=$?
 else
        rm -f "$GIT_DIR/rebased-patches"
 
-       git format-patch -k --stdout --full-index --ignore-if-in-upstream \
+       git format-patch -k --stdout --full-index --cherry-pick --right-only \
                --src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
-               $root_flag "$revisions" >"$GIT_DIR/rebased-patches"
+               "$revisions" ${restrict_revision+^$restrict_revision} \
+               >"$GIT_DIR/rebased-patches"
        ret=$?
 
        if test 0 != $ret
index 7e1eda008815cca80561d6013d41479ea8c90f4a..b64dd28acf86e0b3075a638ae3419fb3a79a1815 100644 (file)
@@ -963,7 +963,7 @@ else
 fi
 git rev-list $merges_option --pretty=oneline --abbrev-commit \
        --abbrev=7 --reverse --left-right --topo-order \
-       $revisions | \
+       $revisions ${restrict_revision+^$restrict_revision} | \
        sed -n "s/^>//p" |
 while read -r shortsha1 rest
 do
index 06c810b64fe04d3675820e38d7eeabe7589dab56..55da9db818665f39ed205d3c77352987e3ca9963 100755 (executable)
@@ -59,6 +59,7 @@ If you prefer to skip this patch, run "git rebase --skip" instead.
 To check out the original branch and stop rebasing, run "git rebase --abort".')
 "
 unset onto
+unset restrict_revision
 cmd=
 strategy=
 strategy_opts=
@@ -546,7 +547,7 @@ then
                        "${switch_to:-HEAD}")
        if test -n "$new_upstream"
        then
-               upstream=$new_upstream
+               restrict_revision=$new_upstream
        fi
 fi
 
@@ -572,7 +573,7 @@ require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
 # and if this is not an interactive rebase.
 mb=$(git merge-base "$onto" "$orig_head")
 if test "$type" != interactive && test "$upstream" = "$onto" &&
-       test "$mb" = "$onto" &&
+       test "$mb" = "$onto" && test -z "$restrict_revision" &&
        # linear history?
        ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
 then
@@ -626,7 +627,7 @@ if test -n "$rebase_root"
 then
        revisions="$onto..$orig_head"
 else
-       revisions="$upstream..$orig_head"
+       revisions="${restrict_revision-$upstream}..$orig_head"
 fi
 
 run_specific_rebase
diff --git a/git.c b/git.c
index 5b6c7611be93c535491bbc9ba027e3acbad0db8f..9c495198317bf31ff40785a48bf0a44253548098 100644 (file)
--- a/git.c
+++ b/git.c
@@ -613,6 +613,8 @@ int main(int argc, char **av)
 
        git_setup_gettext();
 
+       trace_command_performance(argv);
+
        /*
         * "git-xxxx" is the same as "git xxxx", but we obviously:
         *
index d1b8056d8d53c35d62599999026766a4342eb764..f693839cb4786fd5be57d878b58499273ec17f81 100644 (file)
--- a/hashmap.c
+++ b/hashmap.c
@@ -226,3 +226,41 @@ void *hashmap_iter_next(struct hashmap_iter *iter)
                current = iter->map->table[iter->tablepos++];
        }
 }
+
+struct pool_entry {
+       struct hashmap_entry ent;
+       size_t len;
+       unsigned char data[FLEX_ARRAY];
+};
+
+static int pool_entry_cmp(const struct pool_entry *e1,
+                         const struct pool_entry *e2,
+                         const unsigned char *keydata)
+{
+       return e1->data != keydata &&
+              (e1->len != e2->len || memcmp(e1->data, keydata, e1->len));
+}
+
+const void *memintern(const void *data, size_t len)
+{
+       static struct hashmap map;
+       struct pool_entry key, *e;
+
+       /* initialize string pool hashmap */
+       if (!map.tablesize)
+               hashmap_init(&map, (hashmap_cmp_fn) pool_entry_cmp, 0);
+
+       /* lookup interned string in pool */
+       hashmap_entry_init(&key, memhash(data, len));
+       key.len = len;
+       e = hashmap_get(&map, &key, data);
+       if (!e) {
+               /* not found: create it */
+               e = xmallocz(sizeof(struct pool_entry) + len);
+               hashmap_entry_init(e, key.ent.hash);
+               e->len = len;
+               memcpy(e->data, data, len);
+               hashmap_add(&map, e);
+       }
+       return e->data;
+}
index a816ad47b14d2d377ba0e03a3f401ed36a56efa8..ab7958ae333bcc635ba2ac8e40ee8aa6d8814ab4 100644 (file)
--- a/hashmap.h
+++ b/hashmap.h
@@ -13,6 +13,17 @@ extern unsigned int strihash(const char *buf);
 extern unsigned int memhash(const void *buf, size_t len);
 extern unsigned int memihash(const void *buf, size_t len);
 
+static inline unsigned int sha1hash(const unsigned char *sha1)
+{
+       /*
+        * Equivalent to 'return *(unsigned int *)sha1;', but safe on
+        * platforms that don't support unaligned reads.
+        */
+       unsigned int hash;
+       memcpy(&hash, sha1, sizeof(hash));
+       return hash;
+}
+
 /* data structures */
 
 struct hashmap_entry {
@@ -57,6 +68,14 @@ extern void *hashmap_put(struct hashmap *map, void *entry);
 extern void *hashmap_remove(struct hashmap *map, const void *key,
                const void *keydata);
 
+static inline void *hashmap_get_from_hash(const struct hashmap *map,
+               unsigned int hash, const void *keydata)
+{
+       struct hashmap_entry key;
+       hashmap_entry_init(&key, hash);
+       return hashmap_get(map, &key, keydata);
+}
+
 /* hashmap_iter functions */
 
 extern void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);
@@ -68,4 +87,12 @@ static inline void *hashmap_iter_first(struct hashmap *map,
        return hashmap_iter_next(iter);
 }
 
+/* string interning */
+
+extern const void *memintern(const void *data, size_t len);
+static inline const char *strintern(const char *string)
+{
+       return memintern(string, strlen(string));
+}
+
 #endif
diff --git a/help.c b/help.c
index f31f29ac421e224a59726fc1f2dfc16d92faad0f..7af65e205ecdf1a01dce009cbf0ada15de68c844 100644 (file)
--- a/help.c
+++ b/help.c
@@ -144,7 +144,7 @@ static void list_commands_in_dir(struct cmdnames *cmds,
 
        while ((de = readdir(dir)) != NULL) {
                const char *ent;
-               int entlen;
+               size_t entlen;
 
                if (!skip_prefix(de->d_name, prefix, &ent))
                        continue;
@@ -155,8 +155,7 @@ static void list_commands_in_dir(struct cmdnames *cmds,
                        continue;
 
                entlen = strlen(ent);
-               if (has_extension(ent, ".exe"))
-                       entlen -= 4;
+               strip_suffix(ent, ".exe", &entlen);
 
                add_cmdname(cmds, ent, entlen);
        }
index 57290d9bdafc730afaac3ddf8564d85ead39eb31..80790bbaef95a56ac737c7763e48035e3e0754ee 100644 (file)
@@ -610,9 +610,7 @@ int main(int argc, char **argv)
 
                        cmd = c;
                        n = out[0].rm_eo - out[0].rm_so;
-                       cmd_arg = xmalloc(n);
-                       memcpy(cmd_arg, dir + out[0].rm_so + 1, n-1);
-                       cmd_arg[n-1] = '\0';
+                       cmd_arg = xmemdupz(dir + out[0].rm_so + 1, n - 1);
                        dir[out[0].rm_so] = 0;
                        break;
                }
index 6c3cc1725a9461876c7db11b9818eb553afb2fc2..952f8ede49daf4e275c42cc0682bb01c4c58446a 100644 (file)
@@ -199,7 +199,7 @@ static void curl_setup_http(CURL *curl, const char *url,
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
 #ifndef NO_CURL_IOCTL
        curl_easy_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
-       curl_easy_setopt(curl, CURLOPT_IOCTLDATA, &buffer);
+       curl_easy_setopt(curl, CURLOPT_IOCTLDATA, buffer);
 #endif
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_fn);
        curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
diff --git a/khash.h b/khash.h
index 57ff6038c5be0fb5aa28f1b297bf6972bebab350..06c79065490f152ceed2732ba1b93c309eb4d574 100644 (file)
--- a/khash.h
+++ b/khash.h
@@ -320,19 +320,12 @@ static const double __ac_HASH_UPPER = 0.77;
                code;                                                                                           \
        } }
 
-static inline khint_t __kh_oid_hash(const unsigned char *oid)
-{
-       khint_t hash;
-       memcpy(&hash, oid, sizeof(hash));
-       return hash;
-}
-
 #define __kh_oid_cmp(a, b) (hashcmp(a, b) == 0)
 
-KHASH_INIT(sha1, const unsigned char *, void *, 1, __kh_oid_hash, __kh_oid_cmp)
+KHASH_INIT(sha1, const unsigned char *, void *, 1, sha1hash, __kh_oid_cmp)
 typedef kh_sha1_t khash_sha1;
 
-KHASH_INIT(sha1_pos, const unsigned char *, int, 1, __kh_oid_hash, __kh_oid_cmp)
+KHASH_INIT(sha1_pos, const unsigned char *, int, 1, sha1hash, __kh_oid_cmp)
 typedef kh_sha1_pos_t khash_sha1_pos;
 
 #endif /* __AC_KHASH_H */
index afcc98db930a36358283ff199a428b6c5bbbaf8f..1008e722584c882ac86b936307ace701b9225aaf 100644 (file)
@@ -766,17 +766,6 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
        }
 }
 
-static int count_parents(struct commit *commit)
-{
-       struct commit_list *parents = commit->parents;
-       int count = 0;
-       while (parents) {
-               count++;
-               parents = parents->next;
-       }
-       return count;
-}
-
 static void move_diff_queue(struct diff_queue_struct *dst,
                            struct diff_queue_struct *src)
 {
@@ -1150,7 +1139,7 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
        struct commit **parents;
        struct commit_list *p;
        int i;
-       int nparents = count_parents(commit);
+       int nparents = commit_list_count(commit->parents);
 
        diffqueues = xmalloc(nparents * sizeof(*diffqueues));
        cand = xmalloc(nparents * sizeof(*cand));
index 8fbcb6a98aae85b48c6f8de29c6f95af58823411..2564a7f5447b904585f629f0d1233c8a59483a40 100644 (file)
@@ -5,7 +5,6 @@
 #include "sigchain.h"
 
 static struct lock_file *lock_file_list;
-static const char *alternate_index_output;
 
 static void remove_lock_file(void)
 {
@@ -121,7 +120,7 @@ static char *resolve_symlink(char *p, size_t s)
        return p;
 }
 
-
+/* Make sure errno contains a meaningful value on error */
 static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
        /*
@@ -130,8 +129,10 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
         */
        static const size_t max_path_len = sizeof(lk->filename) - 5;
 
-       if (strlen(path) >= max_path_len)
+       if (strlen(path) >= max_path_len) {
+               errno = ENAMETOOLONG;
                return -1;
+       }
        strcpy(lk->filename, path);
        if (!(flags & LOCK_NODEREF))
                resolve_symlink(lk->filename, max_path_len);
@@ -148,44 +149,51 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
                        lock_file_list = lk;
                        lk->on_list = 1;
                }
-               if (adjust_shared_perm(lk->filename))
-                       return error("cannot fix permission bits on %s",
-                                    lk->filename);
+               if (adjust_shared_perm(lk->filename)) {
+                       int save_errno = errno;
+                       error("cannot fix permission bits on %s",
+                             lk->filename);
+                       errno = save_errno;
+                       return -1;
+               }
        }
        else
                lk->filename[0] = 0;
        return lk->fd;
 }
 
-static char *unable_to_lock_message(const char *path, int err)
+void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
 {
-       struct strbuf buf = STRBUF_INIT;
-
        if (err == EEXIST) {
-               strbuf_addf(&buf, "Unable to create '%s.lock': %s.\n\n"
+               strbuf_addf(buf, "Unable to create '%s.lock': %s.\n\n"
                    "If no other git process is currently running, this probably means a\n"
                    "git process crashed in this repository earlier. Make sure no other git\n"
                    "process is running and remove the file manually to continue.",
                            absolute_path(path), strerror(err));
        } else
-               strbuf_addf(&buf, "Unable to create '%s.lock': %s",
+               strbuf_addf(buf, "Unable to create '%s.lock': %s",
                            absolute_path(path), strerror(err));
-       return strbuf_detach(&buf, NULL);
 }
 
 int unable_to_lock_error(const char *path, int err)
 {
-       char *msg = unable_to_lock_message(path, err);
-       error("%s", msg);
-       free(msg);
+       struct strbuf buf = STRBUF_INIT;
+
+       unable_to_lock_message(path, err, &buf);
+       error("%s", buf.buf);
+       strbuf_release(&buf);
        return -1;
 }
 
 NORETURN void unable_to_lock_index_die(const char *path, int err)
 {
-       die("%s", unable_to_lock_message(path, err));
+       struct strbuf buf = STRBUF_INIT;
+
+       unable_to_lock_message(path, err, &buf);
+       die("%s", buf.buf);
 }
 
+/* This should return a meaningful errno on failure */
 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
 {
        int fd = lock_file(lk, path, flags);
@@ -252,25 +260,6 @@ int hold_locked_index(struct lock_file *lk, int die_on_error)
                                         : 0);
 }
 
-void set_alternate_index_output(const char *name)
-{
-       alternate_index_output = name;
-}
-
-int commit_locked_index(struct lock_file *lk)
-{
-       if (alternate_index_output) {
-               if (lk->fd >= 0 && close_lock_file(lk))
-                       return -1;
-               if (rename(lk->filename, alternate_index_output))
-                       return -1;
-               lk->filename[0] = 0;
-               return 0;
-       }
-       else
-               return commit_lock_file(lk);
-}
-
 void rollback_lock_file(struct lock_file *lk)
 {
        if (lk->filename[0]) {
index 10e68442b35c9bd9ca1c00c52609a4827b8bace5..0c53dc11abf5aa10c83b35f07452f3c00a2998d4 100644 (file)
@@ -365,6 +365,7 @@ static void show_sig_lines(struct rev_info *opt, int status, const char *bol)
                eol = strchrnul(bol, '\n');
                printf("%s%.*s%s%s", color, (int)(eol - bol), bol, reset,
                       *eol ? "\n" : "");
+               graph_show_oneline(opt->graph);
                bol = (*eol) ? (eol + 1) : eol;
        }
 }
@@ -413,10 +414,11 @@ static int is_common_merge(const struct commit *commit)
                && !commit->parents->next->next);
 }
 
-static void show_one_mergetag(struct rev_info *opt,
+static void show_one_mergetag(struct commit *commit,
                              struct commit_extra_header *extra,
-                             struct commit *commit)
+                             void *data)
 {
+       struct rev_info *opt = (struct rev_info *)data;
        unsigned char sha1[20];
        struct tag *tag;
        struct strbuf verify_message;
@@ -446,16 +448,17 @@ static void show_one_mergetag(struct rev_info *opt,
 
        payload_size = parse_signature(extra->value, extra->len);
        status = -1;
-       if (extra->len > payload_size)
-               if (verify_signed_buffer(extra->value, payload_size,
-                                        extra->value + payload_size,
-                                        extra->len - payload_size,
-                                        &verify_message, NULL)) {
-                       if (verify_message.len <= gpg_message_offset)
-                               strbuf_addstr(&verify_message, "No signature\n");
-                       else
-                               status = 0;
-               }
+       if (extra->len > payload_size) {
+               /* could have a good signature */
+               if (!verify_signed_buffer(extra->value, payload_size,
+                                         extra->value + payload_size,
+                                         extra->len - payload_size,
+                                         &verify_message, NULL))
+                       status = 0; /* good */
+               else if (verify_message.len <= gpg_message_offset)
+                       strbuf_addstr(&verify_message, "No signature\n");
+               /* otherwise we couldn't verify, which is shown as bad */
+       }
 
        show_sig_lines(opt, status, verify_message.buf);
        strbuf_release(&verify_message);
@@ -463,15 +466,7 @@ static void show_one_mergetag(struct rev_info *opt,
 
 static void show_mergetag(struct rev_info *opt, struct commit *commit)
 {
-       struct commit_extra_header *extra, *to_free;
-
-       to_free = read_commit_extra_headers(commit, NULL);
-       for (extra = to_free; extra; extra = extra->next) {
-               if (strcmp(extra->key, "mergetag"))
-                       continue; /* not a merge tag */
-               show_one_mergetag(opt, extra, commit);
-       }
-       free_commit_extra_headers(to_free);
+       for_each_mergetag(show_one_mergetag, commit, opt);
 }
 
 void show_log(struct rev_info *opt)
index b5c3c5314f8a4b30dc19b12f311383dd189ae3db..1d332b8bbbf076b819e2052c84874fb0728dae02 100644 (file)
@@ -171,7 +171,7 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)
        strbuf_vaddf(&o->obuf, fmt, ap);
        va_end(ap);
 
-       strbuf_add(&o->obuf, "\n", 1);
+       strbuf_addch(&o->obuf, '\n');
        if (!o->buffer_output)
                flush_output(o);
 }
@@ -267,9 +267,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)
                active_cache_tree = cache_tree();
 
        if (!cache_tree_fully_valid(active_cache_tree) &&
-           cache_tree_update(active_cache_tree,
-                             (const struct cache_entry * const *)active_cache,
-                             active_nr, 0) < 0)
+           cache_tree_update(&the_index, 0) < 0)
                die(_("error building trees"));
 
        result = lookup_tree(active_cache_tree->sha1);
@@ -2001,7 +1999,7 @@ int merge_recursive_generic(struct merge_options *o,
                            const unsigned char **base_list,
                            struct commit **result)
 {
-       int clean, index_fd;
+       int clean;
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
        struct commit *head_commit = get_ref(head, o->branch1);
        struct commit *next_commit = get_ref(merge, o->branch2);
@@ -2018,12 +2016,11 @@ int merge_recursive_generic(struct merge_options *o,
                }
        }
 
-       index_fd = hold_locked_index(lock, 1);
+       hold_locked_index(lock, 1);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
        if (active_cache_changed &&
-                       (write_cache(index_fd, active_cache, active_nr) ||
-                        commit_locked_index(lock)))
+           write_locked_index(&the_index, lock, COMMIT_LOCK))
                return error(_("Unable to write index."));
 
        return clean ? 0 : 1;
@@ -2062,12 +2059,9 @@ void init_merge_options(struct merge_options *o)
        if (o->verbosity >= 5)
                o->buffer_output = 0;
        strbuf_init(&o->obuf, 0);
-       memset(&o->current_file_set, 0, sizeof(struct string_list));
-       o->current_file_set.strdup_strings = 1;
-       memset(&o->current_directory_set, 0, sizeof(struct string_list));
-       o->current_directory_set.strdup_strings = 1;
-       memset(&o->df_conflict_file_set, 0, sizeof(struct string_list));
-       o->df_conflict_file_set.strdup_strings = 1;
+       string_list_init(&o->current_file_set, 1);
+       string_list_init(&o->current_directory_set, 1);
+       string_list_init(&o->df_conflict_file_set, 1);
 }
 
 int parse_merge_opt(struct merge_options *o, const char *s)
diff --git a/merge.c b/merge.c
index 1fa6e52bba8de83820b9a658f738630a9d6f250a..74ced7f70b5beec045c134b5719c926fc791f1e2 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -50,13 +50,13 @@ int checkout_fast_forward(const unsigned char *head,
        struct tree *trees[MAX_UNPACK_TREES];
        struct unpack_trees_options opts;
        struct tree_desc t[MAX_UNPACK_TREES];
-       int i, fd, nr_trees = 0;
+       int i, nr_trees = 0;
        struct dir_struct dir;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
        refresh_cache(REFRESH_QUIET);
 
-       fd = hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, 1);
 
        memset(&trees, 0, sizeof(trees));
        memset(&opts, 0, sizeof(opts));
@@ -89,8 +89,7 @@ int checkout_fast_forward(const unsigned char *head,
        }
        if (unpack_trees(nr_trees, t, &opts))
                return -1;
-       if (write_cache(fd, active_cache, active_nr) ||
-               commit_locked_index(lock_file))
+       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
        return 0;
 }
index 49fd508317ebbde5a76f6af64318920ae237c8fd..702cd0518fca67a84417de8268ed70dfa29392e4 100644 (file)
@@ -213,12 +213,11 @@ struct cache_entry *index_dir_exists(struct index_state *istate, const char *nam
 struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int icase)
 {
        struct cache_entry *ce;
-       struct hashmap_entry key;
 
        lazy_init_name_hash(istate);
 
-       hashmap_entry_init(&key, memihash(name, namelen));
-       ce = hashmap_get(&istate->name_hash, &key, NULL);
+       ce = hashmap_get_from_hash(&istate->name_hash,
+                                  memihash(name, namelen), NULL);
        while (ce) {
                if (same_name(ce, name, namelen, icase))
                        return ce;
index 9c31e9a5e0e0d913044501089e09037e47894f03..a16b9f9e936d060ba190b6c7e82ff5f25795f490 100644 (file)
--- a/object.c
+++ b/object.c
@@ -50,18 +50,7 @@ int type_from_string(const char *str)
  */
 static unsigned int hash_obj(const unsigned char *sha1, unsigned int n)
 {
-       unsigned int hash;
-
-       /*
-        * Since the sha1 is essentially random, we just take the
-        * required number of bits directly from the first
-        * sizeof(unsigned int) bytes of sha1.  First we have to copy
-        * the bytes into a properly aligned integer.  If we cared
-        * about getting consistent results across architectures, we
-        * would have to call ntohl() here, too.
-        */
-       memcpy(&hash, sha1, sizeof(unsigned int));
-       return hash & (n - 1);
+       return sha1hash(sha1) & (n - 1);
 }
 
 /*
@@ -141,13 +130,12 @@ static void grow_object_hash(void)
        obj_hash_size = new_hash_size;
 }
 
-void *create_object(const unsigned char *sha1, int type, void *o)
+void *create_object(const unsigned char *sha1, void *o)
 {
        struct object *obj = o;
 
        obj->parsed = 0;
        obj->used = 0;
-       obj->type = type;
        obj->flags = 0;
        hashcpy(obj->sha1, sha1);
 
@@ -159,11 +147,30 @@ void *create_object(const unsigned char *sha1, int type, void *o)
        return obj;
 }
 
+void *object_as_type(struct object *obj, enum object_type type, int quiet)
+{
+       if (obj->type == type)
+               return obj;
+       else if (obj->type == OBJ_NONE) {
+               if (type == OBJ_COMMIT)
+                       ((struct commit *)obj)->index = alloc_commit_index();
+               obj->type = type;
+               return obj;
+       }
+       else {
+               if (!quiet)
+                       error("object %s is a %s, not a %s",
+                             sha1_to_hex(obj->sha1),
+                             typename(obj->type), typename(type));
+               return NULL;
+       }
+}
+
 struct object *lookup_unknown_object(const unsigned char *sha1)
 {
        struct object *obj = lookup_object(sha1);
        if (!obj)
-               obj = create_object(sha1, OBJ_NONE, alloc_object_node());
+               obj = create_object(sha1, alloc_object_node());
        return obj;
 }
 
@@ -214,8 +221,6 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
                warning("object %s has unknown type id %d", sha1_to_hex(sha1), type);
                obj = NULL;
        }
-       if (obj && obj->type == OBJ_NONE)
-               obj->type = type;
        return obj;
 }
 
index 6e12f2c7f4582b5121a4be18be5fedaecd30f38f..5e8d8ee5485a5825c4dc0a0bba1911061723e161 100644 (file)
--- a/object.h
+++ b/object.h
@@ -79,7 +79,9 @@ extern struct object *get_indexed_object(unsigned int);
  */
 struct object *lookup_object(const unsigned char *sha1);
 
-extern void *create_object(const unsigned char *sha1, int type, void *obj);
+extern void *create_object(const unsigned char *sha1, void *obj);
+
+void *object_as_type(struct object *obj, enum object_type type, int quiet);
 
 /*
  * Returns the object, having parsed it to find out what it is.
index 4f36c3204544c40ada8e6ce584deb6063b055518..9992f3ecf249c32e5be31dc047bad46a4c4ac367 100644 (file)
@@ -7,10 +7,9 @@ static uint32_t locate_object_entry_hash(struct packing_data *pdata,
                                         const unsigned char *sha1,
                                         int *found)
 {
-       uint32_t i, hash, mask = (pdata->index_size - 1);
+       uint32_t i, mask = (pdata->index_size - 1);
 
-       memcpy(&hash, sha1, sizeof(uint32_t));
-       i = hash & mask;
+       i = sha1hash(sha1) & mask;
 
        while (pdata->index[i] > 0) {
                uint32_t pos = pdata->index[i] - 1;
diff --git a/path.c b/path.c
index bc804a31b3382e689dd6ff5b4a727b109a691f7a..3afcdb432a009b5e1c869123139192cf1a688292 100644 (file)
--- a/path.c
+++ b/path.c
@@ -249,9 +249,7 @@ int validate_headref(const char *path)
 static struct passwd *getpw_str(const char *username, size_t len)
 {
        struct passwd *pw;
-       char *username_z = xmalloc(len + 1);
-       memcpy(username_z, username, len);
-       username_z[len] = '\0';
+       char *username_z = xmemdupz(username, len);
        pw = getpwnam(username_z);
        free(username_z);
        return pw;
@@ -277,16 +275,16 @@ char *expand_user_path(const char *path)
                        const char *home = getenv("HOME");
                        if (!home)
                                goto return_null;
-                       strbuf_add(&user_path, home, strlen(home));
+                       strbuf_addstr(&user_path, home);
                } else {
                        struct passwd *pw = getpw_str(username, username_len);
                        if (!pw)
                                goto return_null;
-                       strbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));
+                       strbuf_addstr(&user_path, pw->pw_dir);
                }
                to_copy = first_slash;
        }
-       strbuf_add(&user_path, to_copy, strlen(to_copy));
+       strbuf_addstr(&user_path, to_copy);
        return strbuf_detach(&user_path, NULL);
 return_null:
        strbuf_release(&user_path);
index 80430999553d8fda8d095b007bb08c1999bf6993..9304ee33d75ab9de14e4a7b6dc96f5dd8028c464 100644 (file)
@@ -338,7 +338,7 @@ static void NORETURN unsupported_magic(const char *pattern,
                if (!(magic & m->bit))
                        continue;
                if (sb.len)
-                       strbuf_addstr(&sb, " ");
+                       strbuf_addch(&sb, ' ');
                if (short_magic & m->bit)
                        strbuf_addf(&sb, "'%c'", m->mnemonic);
                else
@@ -389,8 +389,7 @@ void parse_pathspec(struct pathspec *pathspec,
                if (!(flags & PATHSPEC_PREFER_CWD))
                        die("BUG: PATHSPEC_PREFER_CWD requires arguments");
 
-               pathspec->items = item = xmalloc(sizeof(*item));
-               memset(item, 0, sizeof(*item));
+               pathspec->items = item = xcalloc(1, sizeof(*item));
                item->match = prefix;
                item->original = prefix;
                item->nowildcard_len = item->len = strlen(prefix);
index bc63b3b80e5b2853f920ba66fe977c7cc0921c78..8bc89b1e0c0206647a8f5ee8da7727f304d14b2f 100644 (file)
@@ -3,7 +3,7 @@
 
 char packet_buffer[LARGE_PACKET_MAX];
 static const char *packet_trace_prefix = "git";
-static const char trace_key[] = "GIT_TRACE_PACKET";
+static struct trace_key trace_packet = TRACE_KEY_INIT(PACKET);
 
 void packet_trace_identity(const char *prog)
 {
@@ -15,7 +15,7 @@ static void packet_trace(const char *buf, unsigned int len, int write)
        int i;
        struct strbuf out;
 
-       if (!trace_want(trace_key))
+       if (!trace_want(&trace_packet))
                return;
 
        /* +32 is just a guess for header + quoting */
@@ -27,7 +27,7 @@ static void packet_trace(const char *buf, unsigned int len, int write)
        if ((len >= 4 && starts_with(buf, "PACK")) ||
            (len >= 5 && starts_with(buf+1, "PACK"))) {
                strbuf_addstr(&out, "PACK ...");
-               unsetenv(trace_key);
+               trace_disable(&trace_packet);
        }
        else {
                /* XXX we should really handle printable utf8 */
@@ -43,7 +43,7 @@ static void packet_trace(const char *buf, unsigned int len, int write)
        }
 
        strbuf_addch(&out, '\n');
-       trace_strbuf(trace_key, &out);
+       trace_strbuf(&trace_packet, &out);
        strbuf_release(&out);
 }
 
index 79ce8a998b0008f9182fa127b85d63feff4592d0..c1fe3a3ef9cba0c155d02475e24315784bd27ae4 100644 (file)
@@ -63,7 +63,7 @@ static void *preload_thread(void *_data)
                        continue;
                ce_mark_uptodate(ce);
        } while (--nr > 0);
-       cache_def_free(&cache);
+       cache_def_clear(&cache);
        return NULL;
 }
 
index 14357e233f3174963add310a29bb35c3de229121..3a1da6fd329efe1723bdb906a907ce4160c4633a 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1376,7 +1376,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
                case trunc_none:
                        break;
                }
-               strbuf_addstr(sb, local_sb.buf);
+               strbuf_addbuf(sb, &local_sb);
        } else {
                int sb_len = sb->len, offset = 0;
                if (c->flush_type == flush_left)
@@ -1554,12 +1554,7 @@ static void pp_header(struct pretty_print_context *pp,
                }
 
                if (!parents_shown) {
-                       struct commit_list *parent;
-                       int num;
-                       for (parent = commit->parents, num = 0;
-                            parent;
-                            parent = parent->next, num++)
-                               ;
+                       unsigned num = commit_list_count(commit->parents);
                        /* with enough slop */
                        strbuf_grow(sb, num * 50 + 20);
                        add_merge_info(pp, sb, commit);
index c9f8c6d2532b23ff07194c99c6f4121a7000bed4..e4365b00d6c3366e6753fc6da3fe1a165ab1222d 100644 (file)
@@ -1,18 +1,30 @@
 #include "cache.h"
-#include "commit.h"
 #include "prio-queue.h"
 
+static inline int compare(struct prio_queue *queue, int i, int j)
+{
+       int cmp = queue->compare(queue->array[i].data, queue->array[j].data,
+                                queue->cb_data);
+       if (!cmp)
+               cmp = queue->array[i].ctr - queue->array[j].ctr;
+       return cmp;
+}
+
+static inline void swap(struct prio_queue *queue, int i, int j)
+{
+       struct prio_queue_entry tmp = queue->array[i];
+       queue->array[i] = queue->array[j];
+       queue->array[j] = tmp;
+}
+
 void prio_queue_reverse(struct prio_queue *queue)
 {
        int i, j;
 
        if (queue->compare != NULL)
                die("BUG: prio_queue_reverse() on non-LIFO queue");
-       for (i = 0; i <= (j = (queue->nr - 1) - i); i++) {
-               struct commit *swap = queue->array[i];
-               queue->array[i] = queue->array[j];
-               queue->array[j] = swap;
-       }
+       for (i = 0; i <= (j = (queue->nr - 1) - i); i++)
+               swap(queue, i, j);
 }
 
 void clear_prio_queue(struct prio_queue *queue)
@@ -21,44 +33,42 @@ void clear_prio_queue(struct prio_queue *queue)
        queue->nr = 0;
        queue->alloc = 0;
        queue->array = NULL;
+       queue->insertion_ctr = 0;
 }
 
 void prio_queue_put(struct prio_queue *queue, void *thing)
 {
-       prio_queue_compare_fn compare = queue->compare;
        int ix, parent;
 
        /* Append at the end */
        ALLOC_GROW(queue->array, queue->nr + 1, queue->alloc);
-       queue->array[queue->nr++] = thing;
-       if (!compare)
+       queue->array[queue->nr].ctr = queue->insertion_ctr++;
+       queue->array[queue->nr].data = thing;
+       queue->nr++;
+       if (!queue->compare)
                return; /* LIFO */
 
        /* Bubble up the new one */
        for (ix = queue->nr - 1; ix; ix = parent) {
                parent = (ix - 1) / 2;
-               if (compare(queue->array[parent], queue->array[ix],
-                           queue->cb_data) <= 0)
+               if (compare(queue, parent, ix) <= 0)
                        break;
 
-               thing = queue->array[parent];
-               queue->array[parent] = queue->array[ix];
-               queue->array[ix] = thing;
+               swap(queue, parent, ix);
        }
 }
 
 void *prio_queue_get(struct prio_queue *queue)
 {
-       void *result, *swap;
+       void *result;
        int ix, child;
-       prio_queue_compare_fn compare = queue->compare;
 
        if (!queue->nr)
                return NULL;
-       if (!compare)
-               return queue->array[--queue->nr]; /* LIFO */
+       if (!queue->compare)
+               return queue->array[--queue->nr].data; /* LIFO */
 
-       result = queue->array[0];
+       result = queue->array[0].data;
        if (!--queue->nr)
                return result;
 
@@ -67,18 +77,14 @@ void *prio_queue_get(struct prio_queue *queue)
        /* Push down the one at the root */
        for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) {
                child = ix * 2 + 1; /* left */
-               if ((child + 1 < queue->nr) &&
-                   (compare(queue->array[child], queue->array[child + 1],
-                            queue->cb_data) >= 0))
+               if (child + 1 < queue->nr &&
+                   compare(queue, child, child + 1) >= 0)
                        child++; /* use right child */
 
-               if (compare(queue->array[ix], queue->array[child],
-                           queue->cb_data) <= 0)
+               if (compare(queue, ix, child) <= 0)
                        break;
 
-               swap = queue->array[child];
-               queue->array[child] = queue->array[ix];
-               queue->array[ix] = swap;
+               swap(queue, child, ix);
        }
        return result;
 }
index 9c3cd1f875ce553c2c10645c9b7df6b4287306f8..d030ec9dd6765447ad986a40945d1ed8bf9272a0 100644 (file)
  */
 typedef int (*prio_queue_compare_fn)(const void *one, const void *two, void *cb_data);
 
+struct prio_queue_entry {
+       unsigned ctr;
+       void *data;
+};
+
 struct prio_queue {
        prio_queue_compare_fn compare;
+       unsigned insertion_ctr;
        void *cb_data;
        int alloc, nr;
-       void **array;
+       struct prio_queue_entry *array;
 };
 
 /*
index 261314ef3cd60662300129df98afff7ea2a1673c..412e6b1ecc36e8bd8b7f090b7da3dcf0c7e4e5bd 100644 (file)
 #include "gettext.h"
 #include "progress.h"
 #include "strbuf.h"
+#include "trace.h"
 
 #define TP_IDX_MAX      8
 
 struct throughput {
        off_t curr_total;
        off_t prev_total;
-       struct timeval prev_tv;
+       uint64_t prev_ns;
        unsigned int avg_bytes;
        unsigned int avg_misecs;
        unsigned int last_bytes[TP_IDX_MAX];
@@ -127,65 +128,65 @@ static void throughput_string(struct strbuf *buf, off_t total,
 void display_throughput(struct progress *progress, off_t total)
 {
        struct throughput *tp;
-       struct timeval tv;
-       unsigned int misecs;
+       uint64_t now_ns;
+       unsigned int misecs, count, rate;
+       struct strbuf buf = STRBUF_INIT;
 
        if (!progress)
                return;
        tp = progress->throughput;
 
-       gettimeofday(&tv, NULL);
+       now_ns = getnanotime();
 
        if (!tp) {
                progress->throughput = tp = calloc(1, sizeof(*tp));
                if (tp) {
                        tp->prev_total = tp->curr_total = total;
-                       tp->prev_tv = tv;
+                       tp->prev_ns = now_ns;
                }
                return;
        }
        tp->curr_total = total;
 
+       /* only update throughput every 0.5 s */
+       if (now_ns - tp->prev_ns <= 500000000)
+               return;
+
        /*
-        * We have x = bytes and y = microsecs.  We want z = KiB/s:
+        * We have x = bytes and y = nanosecs.  We want z = KiB/s:
         *
-        *      z = (x / 1024) / (y / 1000000)
-        *      z = x / y * 1000000 / 1024
-        *      z = x / (y * 1024 / 1000000)
+        *      z = (x / 1024) / (y / 1000000000)
+        *      z = x / y * 1000000000 / 1024
+        *      z = x / (y * 1024 / 1000000000)
         *      z = x / y'
         *
         * To simplify things we'll keep track of misecs, or 1024th of a sec
         * obtained with:
         *
-        *      y' = y * 1024 / 1000000
-        *      y' = y / (1000000 / 1024)
-        *      y' = y / 977
+        *      y' = y * 1024 / 1000000000
+        *      y' = y * (2^10 / 2^42) * (2^42 / 1000000000)
+        *      y' = y / 2^32 * 4398
+        *      y' = (y * 4398) >> 32
         */
-       misecs = (tv.tv_sec - tp->prev_tv.tv_sec) * 1024;
-       misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977;
+       misecs = ((now_ns - tp->prev_ns) * 4398) >> 32;
 
-       if (misecs > 512) {
-               struct strbuf buf = STRBUF_INIT;
-               unsigned int count, rate;
+       count = total - tp->prev_total;
+       tp->prev_total = total;
+       tp->prev_ns = now_ns;
+       tp->avg_bytes += count;
+       tp->avg_misecs += misecs;
+       rate = tp->avg_bytes / tp->avg_misecs;
+       tp->avg_bytes -= tp->last_bytes[tp->idx];
+       tp->avg_misecs -= tp->last_misecs[tp->idx];
+       tp->last_bytes[tp->idx] = count;
+       tp->last_misecs[tp->idx] = misecs;
+       tp->idx = (tp->idx + 1) % TP_IDX_MAX;
 
-               count = total - tp->prev_total;
-               tp->prev_total = total;
-               tp->prev_tv = tv;
-               tp->avg_bytes += count;
-               tp->avg_misecs += misecs;
-               rate = tp->avg_bytes / tp->avg_misecs;
-               tp->avg_bytes -= tp->last_bytes[tp->idx];
-               tp->avg_misecs -= tp->last_misecs[tp->idx];
-               tp->last_bytes[tp->idx] = count;
-               tp->last_misecs[tp->idx] = misecs;
-               tp->idx = (tp->idx + 1) % TP_IDX_MAX;
-
-               throughput_string(&buf, total, rate);
-               strncpy(tp->display, buf.buf, sizeof(tp->display));
-               strbuf_release(&buf);
-               if (progress->last_value != -1 && progress_update)
-                       display(progress, progress->last_value, NULL);
-       }
+       throughput_string(&buf, total, rate);
+       strncpy(tp->display, buf.buf, sizeof(tp->display));
+       strbuf_release(&buf);
+       if (progress->last_value != -1 && progress_update)
+               display(progress, progress->last_value, NULL);
 }
 
 int display_progress(struct progress *progress, unsigned n)
index 6a45966ec499b47efbc76719046b53633e8839a1..5d3c8bd4aaffda9915a3fd62d9d9800f4ac8baff 100644 (file)
@@ -14,6 +14,8 @@
 #include "resolve-undo.h"
 #include "strbuf.h"
 #include "varint.h"
+#include "split-index.h"
+#include "sigchain.h"
 
 static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
                                               unsigned int options);
@@ -34,8 +36,15 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
 #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
 #define CACHE_EXT_TREE 0x54524545      /* "TREE" */
 #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
+#define CACHE_EXT_LINK 0x6c696e6b        /* "link" */
+
+/* changes that can be kept in $GIT_DIR/index (basically all extensions) */
+#define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
+                CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \
+                SPLIT_INDEX_ORDERED)
 
 struct index_state the_index;
+static const char *alternate_index_output;
 
 static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
@@ -47,10 +56,12 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache
 {
        struct cache_entry *old = istate->cache[nr];
 
+       replace_index_entry_in_base(istate, old, ce);
        remove_name_hash(istate, old);
        free(old);
        set_index_entry(istate, nr, ce);
-       istate->cache_changed = 1;
+       ce->ce_flags |= CE_UPDATE_IN_BASE;
+       istate->cache_changed |= CE_ENTRY_CHANGED;
 }
 
 void rename_index_entry_at(struct index_state *istate, int nr, const char *new_name)
@@ -62,9 +73,10 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
        copy_cache_entry(new, old);
        new->ce_flags &= ~CE_HASHED;
        new->ce_namelen = namelen;
+       new->index = 0;
        memcpy(new->name, new_name, namelen + 1);
 
-       cache_tree_invalidate_path(istate->cache_tree, old->name);
+       cache_tree_invalidate_path(istate, old->name);
        remove_index_entry_at(istate, nr);
        add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
@@ -483,8 +495,8 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 
        record_resolve_undo(istate, ce);
        remove_name_hash(istate, ce);
-       free(ce);
-       istate->cache_changed = 1;
+       save_or_free_index_entry(istate, ce);
+       istate->cache_changed |= CE_ENTRY_REMOVED;
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
                return 0;
@@ -507,12 +519,14 @@ void remove_marked_cache_entries(struct index_state *istate)
        for (i = j = 0; i < istate->cache_nr; i++) {
                if (ce_array[i]->ce_flags & CE_REMOVE) {
                        remove_name_hash(istate, ce_array[i]);
-                       free(ce_array[i]);
+                       save_or_free_index_entry(istate, ce_array[i]);
                }
                else
                        ce_array[j++] = ce_array[i];
        }
-       istate->cache_changed = 1;
+       if (j == istate->cache_nr)
+               return;
+       istate->cache_changed |= CE_ENTRY_REMOVED;
        istate->cache_nr = j;
 }
 
@@ -521,7 +535,7 @@ int remove_file_from_index(struct index_state *istate, const char *path)
        int pos = index_name_pos(istate, path, strlen(path));
        if (pos < 0)
                pos = -pos-1;
-       cache_tree_invalidate_path(istate->cache_tree, path);
+       cache_tree_invalidate_path(istate, path);
        while (pos < istate->cache_nr && !strcmp(istate->cache[pos]->name, path))
                remove_index_entry_at(istate, pos);
        return 0;
@@ -570,7 +584,9 @@ static int different_name(struct cache_entry *ce, struct cache_entry *alias)
  * So we use the CE_ADDED flag to verify that the alias was an old
  * one before we accept it as
  */
-static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_entry *alias)
+static struct cache_entry *create_alias_ce(struct index_state *istate,
+                                          struct cache_entry *ce,
+                                          struct cache_entry *alias)
 {
        int len;
        struct cache_entry *new;
@@ -583,7 +599,7 @@ static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_
        new = xcalloc(1, cache_entry_size(len));
        memcpy(new->name, alias->name, len);
        copy_cache_entry(new, ce);
-       free(ce);
+       save_or_free_index_entry(istate, ce);
        return new;
 }
 
@@ -676,7 +692,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                set_object_name_for_intent_to_add_entry(ce);
 
        if (ignore_case && alias && different_name(ce, alias))
-               ce = create_alias_ce(ce, alias);
+               ce = create_alias_ce(istate, ce, alias);
        ce->ce_flags |= CE_ADDED;
 
        /* It was suspected to be racily clean, but it turns out to be Ok */
@@ -939,7 +955,8 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
        int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
        int new_only = option & ADD_CACHE_NEW_ONLY;
 
-       cache_tree_invalidate_path(istate->cache_tree, ce->name);
+       if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
+               cache_tree_invalidate_path(istate, ce->name);
        pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
 
        /* existing match? Just replace it. */
@@ -1002,7 +1019,7 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
                        istate->cache + pos,
                        (istate->cache_nr - pos - 1) * sizeof(ce));
        set_index_entry(istate, pos, ce);
-       istate->cache_changed = 1;
+       istate->cache_changed |= CE_ENTRY_ADDED;
        return 0;
 }
 
@@ -1101,6 +1118,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
            !(ce->ce_flags & CE_VALID))
                updated->ce_flags &= ~CE_VALID;
 
+       /* istate->cache_changed is updated in the caller */
        return updated;
 }
 
@@ -1182,7 +1200,8 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                                 * means the index is not valid anymore.
                                 */
                                ce->ce_flags &= ~CE_VALID;
-                               istate->cache_changed = 1;
+                               ce->ce_flags |= CE_UPDATE_IN_BASE;
+                               istate->cache_changed |= CE_ENTRY_CHANGED;
                        }
                        if (quiet)
                                continue;
@@ -1334,6 +1353,10 @@ static int read_index_extension(struct index_state *istate,
        case CACHE_EXT_RESOLVE_UNDO:
                istate->resolve_undo = resolve_undo_read(data, sz);
                break;
+       case CACHE_EXT_LINK:
+               if (read_link_extension(istate, data, sz))
+                       return -1;
+               break;
        default:
                if (*ext < 'A' || 'Z' < *ext)
                        return error("index uses %.4s extension, which we do not understand",
@@ -1368,6 +1391,7 @@ static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *on
        ce->ce_stat_data.sd_size  = get_be32(&ondisk->size);
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
+       ce->index = 0;
        hashcpy(ce->sha1, ondisk->sha1);
        memcpy(ce->name, name, len);
        ce->name[len] = '\0';
@@ -1442,7 +1466,7 @@ static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk,
 }
 
 /* remember to discard_cache() before reading a different cache! */
-int read_index_from(struct index_state *istate, const char *path)
+int do_read_index(struct index_state *istate, const char *path, int must_exist)
 {
        int fd, i;
        struct stat st;
@@ -1459,9 +1483,9 @@ int read_index_from(struct index_state *istate, const char *path)
        istate->timestamp.nsec = 0;
        fd = open(path, O_RDONLY);
        if (fd < 0) {
-               if (errno == ENOENT)
+               if (!must_exist && errno == ENOENT)
                        return 0;
-               die_errno("index file open failed");
+               die_errno("%s: index file open failed", path);
        }
 
        if (fstat(fd, &st))
@@ -1480,7 +1504,7 @@ int read_index_from(struct index_state *istate, const char *path)
        if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
 
-       hashcpy(istate->sha1, (unsigned char *)hdr + mmap_size - 20);
+       hashcpy(istate->sha1, (const unsigned char *)hdr + mmap_size - 20);
        istate->version = ntohl(hdr->hdr_version);
        istate->cache_nr = ntohl(hdr->hdr_entries);
        istate->cache_alloc = alloc_nr(istate->cache_nr);
@@ -1534,6 +1558,40 @@ int read_index_from(struct index_state *istate, const char *path)
        die("index file corrupt");
 }
 
+int read_index_from(struct index_state *istate, const char *path)
+{
+       struct split_index *split_index;
+       int ret;
+
+       /* istate->initialized covers both .git/index and .git/sharedindex.xxx */
+       if (istate->initialized)
+               return istate->cache_nr;
+
+       ret = do_read_index(istate, path, 0);
+       split_index = istate->split_index;
+       if (!split_index)
+               return ret;
+
+       if (is_null_sha1(split_index->base_sha1))
+               return ret;
+
+       if (split_index->base)
+               discard_index(split_index->base);
+       else
+               split_index->base = xcalloc(1, sizeof(*split_index->base));
+       ret = do_read_index(split_index->base,
+                           git_path("sharedindex.%s",
+                                    sha1_to_hex(split_index->base_sha1)), 1);
+       if (hashcmp(split_index->base_sha1, split_index->base->sha1))
+               die("broken index, expect %s in %s, got %s",
+                   sha1_to_hex(split_index->base_sha1),
+                   git_path("sharedindex.%s",
+                                    sha1_to_hex(split_index->base_sha1)),
+                   sha1_to_hex(split_index->base->sha1));
+       merge_base_index(istate);
+       return ret;
+}
+
 int is_index_unborn(struct index_state *istate)
 {
        return (!istate->cache_nr && !istate->timestamp.sec);
@@ -1543,8 +1601,15 @@ int discard_index(struct index_state *istate)
 {
        int i;
 
-       for (i = 0; i < istate->cache_nr; i++)
+       for (i = 0; i < istate->cache_nr; i++) {
+               if (istate->cache[i]->index &&
+                   istate->split_index &&
+                   istate->split_index->base &&
+                   istate->cache[i]->index <= istate->split_index->base->cache_nr &&
+                   istate->cache[i] == istate->split_index->base->cache[istate->cache[i]->index - 1])
+                       continue;
                free(istate->cache[i]);
+       }
        resolve_undo_clear_index(istate);
        istate->cache_nr = 0;
        istate->cache_changed = 0;
@@ -1556,6 +1621,7 @@ int discard_index(struct index_state *istate)
        free(istate->cache);
        istate->cache = NULL;
        istate->cache_alloc = 0;
+       discard_split_index(istate);
        return 0;
 }
 
@@ -1616,7 +1682,7 @@ static int write_index_ext_header(git_SHA_CTX *context, int fd,
                (ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
 }
 
-static int ce_flush(git_SHA_CTX *context, int fd)
+static int ce_flush(git_SHA_CTX *context, int fd, unsigned char *sha1)
 {
        unsigned int left = write_buffer_len;
 
@@ -1634,6 +1700,7 @@ static int ce_flush(git_SHA_CTX *context, int fd)
 
        /* Append the SHA1 signature at the end */
        git_SHA1_Final(write_buffer + left, context);
+       hashcpy(sha1, write_buffer + left);
        left += 20;
        return (write_in_full(fd, write_buffer, left) != left) ? -1 : 0;
 }
@@ -1705,7 +1772,7 @@ static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
        ondisk->size = htonl(ce->ce_stat_data.sd_size);
        hashcpy(ondisk->sha1, ce->sha1);
 
-       flags = ce->ce_flags;
+       flags = ce->ce_flags & ~CE_NAMEMASK;
        flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
        ondisk->flags = htons(flags);
        if (ce->ce_flags & CE_EXTENDED) {
@@ -1724,9 +1791,15 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
 {
        int size;
        struct ondisk_cache_entry *ondisk;
+       int saved_namelen = saved_namelen; /* compiler workaround */
        char *name;
        int result;
 
+       if (ce->ce_flags & CE_STRIP_NAME) {
+               saved_namelen = ce_namelen(ce);
+               ce->ce_namelen = 0;
+       }
+
        if (!previous_name) {
                size = ondisk_ce_size(ce);
                ondisk = xcalloc(1, size);
@@ -1758,6 +1831,10 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
                strbuf_splice(previous_name, common, to_remove,
                              ce->name + common, ce_namelen(ce) - common);
        }
+       if (ce->ce_flags & CE_STRIP_NAME) {
+               ce->ce_namelen = saved_namelen;
+               ce->ce_flags &= ~CE_STRIP_NAME;
+       }
 
        result = ce_write(c, fd, ondisk, size);
        free(ondisk);
@@ -1827,13 +1904,13 @@ static int has_racy_timestamp(struct index_state *istate)
 void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
 {
        if ((istate->cache_changed || has_racy_timestamp(istate)) &&
-           verify_index(istate) && !write_index(istate, lockfile->fd))
-               commit_locked_index(lockfile);
-       else
+           verify_index(istate) &&
+           write_locked_index(istate, lockfile, COMMIT_LOCK))
                rollback_lock_file(lockfile);
 }
 
-int write_index(struct index_state *istate, int newfd)
+static int do_write_index(struct index_state *istate, int newfd,
+                         int strip_extensions)
 {
        git_SHA_CTX c;
        struct cache_header hdr;
@@ -1855,8 +1932,11 @@ int write_index(struct index_state *istate, int newfd)
                }
        }
 
-       if (!istate->version)
+       if (!istate->version) {
                istate->version = get_index_format_default();
+               if (getenv("GIT_TEST_SPLIT_INDEX"))
+                       init_split_index(istate);
+       }
 
        /* demote version 3 to version 2 when the latter suffices */
        if (istate->version == 3 || istate->version == 2)
@@ -1896,7 +1976,18 @@ int write_index(struct index_state *istate, int newfd)
        strbuf_release(&previous_name_buf);
 
        /* Write extension data here */
-       if (istate->cache_tree) {
+       if (!strip_extensions && istate->split_index) {
+               struct strbuf sb = STRBUF_INIT;
+
+               err = write_link_extension(&sb, istate) < 0 ||
+                       write_index_ext_header(&c, newfd, CACHE_EXT_LINK,
+                                              sb.len) < 0 ||
+                       ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               strbuf_release(&sb);
+               if (err)
+                       return -1;
+       }
+       if (!strip_extensions && istate->cache_tree) {
                struct strbuf sb = STRBUF_INIT;
 
                cache_tree_write(&sb, istate->cache_tree);
@@ -1906,7 +1997,7 @@ int write_index(struct index_state *istate, int newfd)
                if (err)
                        return -1;
        }
-       if (istate->resolve_undo) {
+       if (!strip_extensions && istate->resolve_undo) {
                struct strbuf sb = STRBUF_INIT;
 
                resolve_undo_write(&sb, istate->resolve_undo);
@@ -1918,13 +2009,138 @@ int write_index(struct index_state *istate, int newfd)
                        return -1;
        }
 
-       if (ce_flush(&c, newfd) || fstat(newfd, &st))
+       if (ce_flush(&c, newfd, istate->sha1) || fstat(newfd, &st))
                return -1;
        istate->timestamp.sec = (unsigned int)st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
        return 0;
 }
 
+void set_alternate_index_output(const char *name)
+{
+       alternate_index_output = name;
+}
+
+static int commit_locked_index(struct lock_file *lk)
+{
+       if (alternate_index_output) {
+               if (lk->fd >= 0 && close_lock_file(lk))
+                       return -1;
+               if (rename(lk->filename, alternate_index_output))
+                       return -1;
+               lk->filename[0] = 0;
+               return 0;
+       } else {
+               return commit_lock_file(lk);
+       }
+}
+
+static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
+                                unsigned flags)
+{
+       int ret = do_write_index(istate, lock->fd, 0);
+       if (ret)
+               return ret;
+       assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
+              (COMMIT_LOCK | CLOSE_LOCK));
+       if (flags & COMMIT_LOCK)
+               return commit_locked_index(lock);
+       else if (flags & CLOSE_LOCK)
+               return close_lock_file(lock);
+       else
+               return ret;
+}
+
+static int write_split_index(struct index_state *istate,
+                            struct lock_file *lock,
+                            unsigned flags)
+{
+       int ret;
+       prepare_to_write_split_index(istate);
+       ret = do_write_locked_index(istate, lock, flags);
+       finish_writing_split_index(istate);
+       return ret;
+}
+
+static char *temporary_sharedindex;
+
+static void remove_temporary_sharedindex(void)
+{
+       if (temporary_sharedindex) {
+               unlink_or_warn(temporary_sharedindex);
+               free(temporary_sharedindex);
+               temporary_sharedindex = NULL;
+       }
+}
+
+static void remove_temporary_sharedindex_on_signal(int signo)
+{
+       remove_temporary_sharedindex();
+       sigchain_pop(signo);
+       raise(signo);
+}
+
+static int write_shared_index(struct index_state *istate,
+                             struct lock_file *lock, unsigned flags)
+{
+       struct split_index *si = istate->split_index;
+       static int installed_handler;
+       int fd, ret;
+
+       temporary_sharedindex = git_pathdup("sharedindex_XXXXXX");
+       fd = mkstemp(temporary_sharedindex);
+       if (fd < 0) {
+               free(temporary_sharedindex);
+               temporary_sharedindex = NULL;
+               hashclr(si->base_sha1);
+               return do_write_locked_index(istate, lock, flags);
+       }
+       if (!installed_handler) {
+               atexit(remove_temporary_sharedindex);
+               sigchain_push_common(remove_temporary_sharedindex_on_signal);
+       }
+       move_cache_to_base_index(istate);
+       ret = do_write_index(si->base, fd, 1);
+       close(fd);
+       if (ret) {
+               remove_temporary_sharedindex();
+               return ret;
+       }
+       ret = rename(temporary_sharedindex,
+                    git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
+       free(temporary_sharedindex);
+       temporary_sharedindex = NULL;
+       if (!ret)
+               hashcpy(si->base_sha1, si->base->sha1);
+       return ret;
+}
+
+int write_locked_index(struct index_state *istate, struct lock_file *lock,
+                      unsigned flags)
+{
+       struct split_index *si = istate->split_index;
+
+       if (!si || alternate_index_output ||
+           (istate->cache_changed & ~EXTMASK)) {
+               if (si)
+                       hashclr(si->base_sha1);
+               return do_write_locked_index(istate, lock, flags);
+       }
+
+       if (getenv("GIT_TEST_SPLIT_INDEX")) {
+               int v = si->base_sha1[0];
+               if ((v & 15) < 6)
+                       istate->cache_changed |= SPLIT_INDEX_ORDERED;
+       }
+       if (istate->cache_changed & SPLIT_INDEX_ORDERED) {
+               int ret = write_shared_index(istate, lock, flags);
+               if (ret)
+                       return ret;
+       }
+
+       return write_split_index(istate, lock, flags);
+}
+
 /*
  * Read the index file that is potentially unmerged into given
  * index_state, dropping any unmerged entries.  Returns true if
diff --git a/refs.c b/refs.c
index 82e4842a36ef803d6406f852f2415b3b77f477f7..27927f2319130cc0575817542dfd47c37cc5149b 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -7,27 +7,21 @@
 
 /*
  * How to handle various characters in refnames:
- * This table is used by both the SIMD and non-SIMD code.  It has
- * some cases that are only useful for the SIMD; these are handled
- * equivalently to the listed disposition in the non-SIMD code.
  * 0: An acceptable character for refs
- * 1: @, look for a following { to reject @{ in refs (SIMD or = 0)
- * 2: \0: End-of-component and string
- * 3: /: End-of-component (SIMD or = 2)
- * 4: ., look for a preceding . to reject .. in refs
- * 5: {, look for a preceding @ to reject @{ in refs
- * 6: *, usually a bad character except, once as a wildcard (SIMD or = 7)
- * 7: A bad character except * (see check_refname_component below)
+ * 1: End-of-component
+ * 2: ., look for a preceding . to reject .. in refs
+ * 3: {, look for a preceding @ to reject @{ in refs
+ * 4: A bad character: ASCII control characters, "~", "^", ":" or SP
  */
 static unsigned char refname_disposition[256] = {
-       2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-       7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 4, 3,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 7,
-       1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 7, 0,
+       1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+       4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 7, 7
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4
 };
 
 /*
@@ -39,9 +33,8 @@ static unsigned char refname_disposition[256] = {
  * - any path component of it begins with ".", or
  * - it has double dots "..", or
  * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
- * - it has pattern-matching notation "*", "?", "[", anywhere, or
- * - it ends with a "/", or
- * - it ends with ".lock", or
+ * - it ends with a "/".
+ * - it ends with ".lock"
  * - it contains a "\" (backslash)
  */
 static int check_refname_component(const char *refname, int flags)
@@ -53,19 +46,17 @@ static int check_refname_component(const char *refname, int flags)
                int ch = *cp & 255;
                unsigned char disp = refname_disposition[ch];
                switch (disp) {
-               case 2: /* fall-through */
-               case 3:
+               case 1:
                        goto out;
-               case 4:
+               case 2:
                        if (last == '.')
                                return -1; /* Refname contains "..". */
                        break;
-               case 5:
+               case 3:
                        if (last == '@')
                                return -1; /* Refname contains "@{". */
                        break;
-               case 6: /* fall-through */
-               case 7:
+               case 4:
                        return -1;
                }
                last = ch;
@@ -88,7 +79,7 @@ static int check_refname_component(const char *refname, int flags)
        return cp - refname;
 }
 
-static int check_refname_format_bytewise(const char *refname, int flags)
+int check_refname_format(const char *refname, int flags)
 {
        int component_len, component_count = 0;
 
@@ -124,196 +115,6 @@ static int check_refname_format_bytewise(const char *refname, int flags)
        return 0;
 }
 
-#if defined(__GNUC__) && defined(__x86_64__)
-#define SSE_VECTOR_BYTES 16
-
-/* Vectorized version of check_refname_format. */
-int check_refname_format(const char *refname, int flags)
-{
-       const char *cp = refname;
-
-       const __m128i dot = _mm_set1_epi8('.');
-       const __m128i at = _mm_set1_epi8('@');
-       const __m128i curly = _mm_set1_epi8('{');
-       const __m128i slash = _mm_set1_epi8('/');
-       const __m128i zero = _mm_set1_epi8('\000');
-       const __m128i el = _mm_set1_epi8('l');
-
-       /* below '*', all characters are forbidden or rare */
-       const __m128i star_ub = _mm_set1_epi8('*' + 1);
-
-       const __m128i colon = _mm_set1_epi8(':');
-       const __m128i question = _mm_set1_epi8('?');
-
-       /* '['..'^' contains 4 characters: 3 forbidden and 1 rare */
-       const __m128i bracket_lb = _mm_set1_epi8('[' - 1);
-       const __m128i caret_ub = _mm_set1_epi8('^' + 1);
-
-       /* '~' and above are forbidden */
-       const __m128i tilde_lb = _mm_set1_epi8('~' - 1);
-
-       int component_count = 0;
-       int orig_flags = flags;
-
-       if (refname[0] == 0 || refname[0] == '/') {
-               /* entirely empty ref or initial ref component */
-               return -1;
-       }
-
-       /*
-        * Initial ref component of '.'; below we look for /. so we'll
-        * miss this.
-        */
-       if (refname[0] == '.') {
-               if (refname[1] == '/' || refname[1] == '\0')
-                       return -1;
-               if (!(flags & REFNAME_DOT_COMPONENT))
-                       return -1;
-       }
-       while(1) {
-               __m128i tmp, tmp1, result;
-               uint64_t mask;
-
-               if ((uintptr_t) cp % PAGE_SIZE > PAGE_SIZE - SSE_VECTOR_BYTES  - 1)
-                       /*
-                        * End-of-page; fall back to slow method for
-                        * this entire ref.
-                        */
-                       return check_refname_format_bytewise(refname, orig_flags);
-
-               tmp = _mm_loadu_si128((__m128i *)cp);
-               tmp1 = _mm_loadu_si128((__m128i *)(cp + 1));
-
-               /*
-                * This range (note the lt) contains some
-                * permissible-but-rare characters (including all
-                * characters >= 128), which we handle later.  It also
-                * includes \000.
-                */
-               result = _mm_cmplt_epi8(tmp, star_ub);
-
-               result = _mm_or_si128(result, _mm_cmpeq_epi8(tmp, question));
-               result = _mm_or_si128(result, _mm_cmpeq_epi8(tmp, colon));
-
-               /* This range contains the permissible ] as bycatch */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpgt_epi8(tmp, bracket_lb),
-                                             _mm_cmplt_epi8(tmp, caret_ub)));
-
-               result = _mm_or_si128(result, _mm_cmpgt_epi8(tmp, tilde_lb));
-
-               /* .. */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, dot),
-                                             _mm_cmpeq_epi8(tmp1, dot)));
-               /* @{ */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, at),
-                                             _mm_cmpeq_epi8(tmp1, curly)));
-               /* // */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, slash),
-                                             _mm_cmpeq_epi8(tmp1, slash)));
-               /* trailing / */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, slash),
-                                             _mm_cmpeq_epi8(tmp1, zero)));
-               /* .l, beginning of .lock */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, dot),
-                                             _mm_cmpeq_epi8(tmp1, el)));
-               /*
-                * Even though /. is not necessarily an error, we flag
-                * it anyway. If we find it, we'll check if it's valid
-                * and if so we'll advance just past it.
-                */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, slash),
-                                             _mm_cmpeq_epi8(tmp1, dot)));
-
-               mask = _mm_movemask_epi8(result);
-               if (mask) {
-                       /*
-                        * We've found either end-of-string, or some
-                        * probably-bad character or substring.
-                        */
-                       int i = __builtin_ctz(mask);
-                       switch (refname_disposition[cp[i] & 255]) {
-                       case 0: /* fall-through */
-                       case 5:
-                               /*
-                                * bycatch: a good character that's in
-                                * one of the ranges of mostly-forbidden
-                                * characters
-                                */
-                               cp += i + 1;
-                               break;
-                       case 1:
-                               if (cp[i + 1] == '{')
-                                       return -1;
-                               cp += i + 1;
-                               break;
-                       case 2:
-                               if (!(flags & REFNAME_ALLOW_ONELEVEL)
-                                   && !component_count && !strchr(refname, '/'))
-                                       /* Refname has only one component. */
-                                       return -1;
-                               return 0;
-                       case 3:
-                               component_count ++;
-                               /*
-                                * Even if leading dots are allowed, don't
-                                * allow "." as a component (".." is
-                                * prevented by case 4 below).
-                                */
-                               if (cp[i + 1] == '.') {
-                                       if (cp[i + 2] == '\0')
-                                               return -1;
-                                       if (flags & REFNAME_DOT_COMPONENT) {
-                                               /* skip to just after the /. */
-                                               cp += i + 2;
-                                               break;
-                                       }
-                                       return -1;
-                               } else if (cp[i + 1] == '/' || cp[i + 1] == '\0')
-                                       return -1;
-                               break;
-                       case 4:
-                               if (cp[i + 1] == '.' || cp[i + 1] == '\0')
-                                       return -1;
-                               /* .lock as end-of-component or end-of-string */
-                               if ((!strncmp(cp + i, ".lock", 5))
-                                   && (cp[i + 5] == '/' || cp[i + 5] == 0))
-                                       return -1;
-                               cp += 1;
-                               break;
-                       case 6:
-                               if (((cp == refname + i) || cp[i - 1] == '/')
-                                   && (cp[i + 1] == '/' || cp[i + 1] == 0))
-                                       if (flags & REFNAME_REFSPEC_PATTERN) {
-                                               flags &= ~REFNAME_REFSPEC_PATTERN;
-                                               /* restart after the * */
-                                               cp += i + 1;
-                                               continue;
-                                       }
-                               /* fall-through */
-                       case 7:
-                               return -1;
-                       }
-               } else
-                       cp += SSE_VECTOR_BYTES;
-       }
-}
-
-#else
-
-int check_refname_format (const char *refname, int flags)
-{
-       return check_refname_format_bytewise(refname, flags);
-}
-
-#endif
-
 struct ref_entry;
 
 /*
@@ -1361,7 +1162,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 
                if (de->d_name[0] == '.')
                        continue;
-               if (has_extension(de->d_name, ".lock"))
+               if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(&refname, de->d_name);
                refdir = *refs->name
@@ -1533,6 +1334,7 @@ static const char *handle_missing_loose_ref(const char *refname,
        }
 }
 
+/* This function needs to return a meaningful errno on failure */
 const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
 {
        int depth = MAXDEPTH;
@@ -1543,8 +1345,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
        if (flag)
                *flag = 0;
 
-       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
+       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+               errno = EINVAL;
                return NULL;
+       }
 
        for (;;) {
                char path[PATH_MAX];
@@ -1552,8 +1356,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                char *buf;
                int fd;
 
-               if (--depth < 0)
+               if (--depth < 0) {
+                       errno = ELOOP;
                        return NULL;
+               }
 
                git_snpath(path, sizeof(path), "%s", refname);
 
@@ -1615,9 +1421,13 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                                return NULL;
                }
                len = read_in_full(fd, buffer, sizeof(buffer)-1);
-               close(fd);
-               if (len < 0)
+               if (len < 0) {
+                       int save_errno = errno;
+                       close(fd);
+                       errno = save_errno;
                        return NULL;
+               }
+               close(fd);
                while (len && isspace(buffer[len-1]))
                        len--;
                buffer[len] = '\0';
@@ -1634,6 +1444,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                            (buffer[40] != '\0' && !isspace(buffer[40]))) {
                                if (flag)
                                        *flag |= REF_ISBROKEN;
+                               errno = EINVAL;
                                return NULL;
                        }
                        return refname;
@@ -1646,6 +1457,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
                        if (flag)
                                *flag |= REF_ISBROKEN;
+                       errno = EINVAL;
                        return NULL;
                }
                refname = strcpy(refname_buffer, buf);
@@ -1730,9 +1542,8 @@ static enum peel_status peel_object(const unsigned char *name, unsigned char *sh
 
        if (o->type == OBJ_NONE) {
                int type = sha1_object_info(name, NULL);
-               if (type < 0)
+               if (type < 0 || !object_as_type(o, type, 0))
                        return PEEL_INVALID;
-               o->type = type;
        }
 
        if (o->type != OBJ_TAG)
@@ -2131,18 +1942,22 @@ int refname_match(const char *abbrev_name, const char *full_name)
        return 0;
 }
 
+/* This function should make sure errno is meaningful on error */
 static struct ref_lock *verify_lock(struct ref_lock *lock,
        const unsigned char *old_sha1, int mustexist)
 {
        if (read_ref_full(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+               int save_errno = errno;
                error("Can't verify ref %s", lock->ref_name);
                unlock_ref(lock);
+               errno = save_errno;
                return NULL;
        }
        if (hashcmp(lock->old_sha1, old_sha1)) {
                error("Ref %s is at %s but expected %s", lock->ref_name,
                        sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
                unlock_ref(lock);
+               errno = EBUSY;
                return NULL;
        }
        return lock;
@@ -2155,14 +1970,16 @@ static int remove_empty_directories(const char *file)
         * only empty directories), remove them.
         */
        struct strbuf path;
-       int result;
+       int result, save_errno;
 
        strbuf_init(&path, 20);
        strbuf_addstr(&path, file);
 
        result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
+       save_errno = errno;
 
        strbuf_release(&path);
+       errno = save_errno;
 
        return result;
 }
@@ -2251,6 +2068,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
        return logs_found;
 }
 
+/* This function should make sure errno is meaningful on error */
 static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                                            const unsigned char *old_sha1,
                                            int flags, int *type_p)
@@ -2411,6 +2229,7 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
        return 0;
 }
 
+/* This should return a meaningful errno on failure */
 int lock_packed_refs(int flags)
 {
        struct packed_ref_cache *packed_ref_cache;
@@ -2430,11 +2249,16 @@ int lock_packed_refs(int flags)
        return 0;
 }
 
+/*
+ * Commit the packed refs changes.
+ * On error we must make sure that errno contains a meaningful value.
+ */
 int commit_packed_refs(void)
 {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
        int error = 0;
+       int save_errno = 0;
 
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
@@ -2444,10 +2268,13 @@ int commit_packed_refs(void)
        do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
                                 0, write_packed_entry_fn,
                                 &packed_ref_cache->lock->fd);
-       if (commit_lock_file(packed_ref_cache->lock))
+       if (commit_lock_file(packed_ref_cache->lock)) {
+               save_errno = errno;
                error = -1;
+       }
        packed_ref_cache->lock = NULL;
        release_packed_ref_cache(packed_ref_cache);
+       errno = save_errno;
        return error;
 }
 
@@ -2654,12 +2481,12 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
        return 0;
 }
 
-int repack_without_refs(const char **refnames, int n)
+int repack_without_refs(const char **refnames, int n, struct strbuf *err)
 {
        struct ref_dir *packed;
        struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
        struct string_list_item *ref_to_delete;
-       int i, removed = 0;
+       int i, ret, removed = 0;
 
        /* Look for a packed ref */
        for (i = 0; i < n; i++)
@@ -2671,6 +2498,11 @@ int repack_without_refs(const char **refnames, int n)
                return 0; /* no refname exists in packed refs */
 
        if (lock_packed_refs(0)) {
+               if (err) {
+                       unable_to_lock_message(git_path("packed-refs"), errno,
+                                              err);
+                       return -1;
+               }
                unable_to_lock_error(git_path("packed-refs"), errno);
                return error("cannot delete '%s' from packed refs", refnames[i]);
        }
@@ -2697,12 +2529,16 @@ int repack_without_refs(const char **refnames, int n)
        }
 
        /* Write what remains */
-       return commit_packed_refs();
+       ret = commit_packed_refs();
+       if (ret && err)
+               strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
+                           strerror(errno));
+       return ret;
 }
 
 static int repack_without_ref(const char *refname)
 {
-       return repack_without_refs(&refname, 1);
+       return repack_without_refs(&refname, 1, NULL);
 }
 
 static int delete_ref_loose(struct ref_lock *lock, int flag)
@@ -2940,6 +2776,7 @@ static int copy_msg(char *buf, const char *msg)
        return cp - buf;
 }
 
+/* This function must set a meaningful errno on failure */
 int log_ref_setup(const char *refname, char *logfile, int bufsize)
 {
        int logfd, oflags = O_APPEND | O_WRONLY;
@@ -2950,9 +2787,12 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)
             starts_with(refname, "refs/remotes/") ||
             starts_with(refname, "refs/notes/") ||
             !strcmp(refname, "HEAD"))) {
-               if (safe_create_leading_directories(logfile) < 0)
-                       return error("unable to create directory for %s",
-                                    logfile);
+               if (safe_create_leading_directories(logfile) < 0) {
+                       int save_errno = errno;
+                       error("unable to create directory for %s", logfile);
+                       errno = save_errno;
+                       return -1;
+               }
                oflags |= O_CREAT;
        }
 
@@ -2963,15 +2803,22 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)
 
                if ((oflags & O_CREAT) && errno == EISDIR) {
                        if (remove_empty_directories(logfile)) {
-                               return error("There are still logs under '%s'",
-                                            logfile);
+                               int save_errno = errno;
+                               error("There are still logs under '%s'",
+                                     logfile);
+                               errno = save_errno;
+                               return -1;
                        }
                        logfd = open(logfile, oflags, 0666);
                }
 
-               if (logfd < 0)
-                       return error("Unable to append to %s: %s",
-                                    logfile, strerror(errno));
+               if (logfd < 0) {
+                       int save_errno = errno;
+                       error("Unable to append to %s: %s", logfile,
+                             strerror(errno));
+                       errno = save_errno;
+                       return -1;
+               }
        }
 
        adjust_shared_perm(logfile);
@@ -3011,24 +2858,38 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
                len += copy_msg(logrec + len - 1, msg) - 1;
        written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
        free(logrec);
-       if (close(logfd) != 0 || written != len)
-               return error("Unable to append to %s", log_file);
+       if (written != len) {
+               int save_errno = errno;
+               close(logfd);
+               error("Unable to append to %s", log_file);
+               errno = save_errno;
+               return -1;
+       }
+       if (close(logfd)) {
+               int save_errno = errno;
+               error("Unable to append to %s", log_file);
+               errno = save_errno;
+               return -1;
+       }
        return 0;
 }
 
-static int is_branch(const char *refname)
+int is_branch(const char *refname)
 {
        return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
 }
 
+/* This function must return a meaningful errno */
 int write_ref_sha1(struct ref_lock *lock,
        const unsigned char *sha1, const char *logmsg)
 {
        static char term = '\n';
        struct object *o;
 
-       if (!lock)
+       if (!lock) {
+               errno = EINVAL;
                return -1;
+       }
        if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) {
                unlock_ref(lock);
                return 0;
@@ -3038,19 +2899,23 @@ int write_ref_sha1(struct ref_lock *lock,
                error("Trying to write ref %s with nonexistent object %s",
                        lock->ref_name, sha1_to_hex(sha1));
                unlock_ref(lock);
+               errno = EINVAL;
                return -1;
        }
        if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
                error("Trying to write non-commit object %s to branch %s",
                        sha1_to_hex(sha1), lock->ref_name);
                unlock_ref(lock);
+               errno = EINVAL;
                return -1;
        }
        if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
-           write_in_full(lock->lock_fd, &term, 1) != 1
-               || close_ref(lock) < 0) {
+           write_in_full(lock->lock_fd, &term, 1) != 1 ||
+           close_ref(lock) < 0) {
+               int save_errno = errno;
                error("Couldn't write %s", lock->lk->filename);
                unlock_ref(lock);
+               errno = save_errno;
                return -1;
        }
        clear_loose_ref_cache(&ref_cache);
@@ -3432,7 +3297,7 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data
 
                if (de->d_name[0] == '.')
                        continue;
-               if (has_extension(de->d_name, ".lock"))
+               if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(name, de->d_name);
                if (stat(git_path("logs/%s", name->buf), &st) < 0) {
@@ -3487,10 +3352,13 @@ static struct ref_lock *update_ref_lock(const char *refname,
 
 static int update_ref_write(const char *action, const char *refname,
                            const unsigned char *sha1, struct ref_lock *lock,
-                           enum action_on_err onerr)
+                           struct strbuf *err, enum action_on_err onerr)
 {
        if (write_ref_sha1(lock, sha1, action) < 0) {
                const char *str = "Cannot update the ref '%s'.";
+               if (err)
+                       strbuf_addf(err, str, refname);
+
                switch (onerr) {
                case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
                case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
@@ -3533,10 +3401,13 @@ struct ref_transaction *ref_transaction_begin(void)
        return xcalloc(1, sizeof(struct ref_transaction));
 }
 
-static void ref_transaction_free(struct ref_transaction *transaction)
+void ref_transaction_free(struct ref_transaction *transaction)
 {
        int i;
 
+       if (!transaction)
+               return;
+
        for (i = 0; i < transaction->nr; i++)
                free(transaction->updates[i]);
 
@@ -3544,11 +3415,6 @@ static void ref_transaction_free(struct ref_transaction *transaction)
        free(transaction);
 }
 
-void ref_transaction_rollback(struct ref_transaction *transaction)
-{
-       ref_transaction_free(transaction);
-}
-
 static struct ref_update *add_update(struct ref_transaction *transaction,
                                     const char *refname)
 {
@@ -3561,23 +3427,30 @@ static struct ref_update *add_update(struct ref_transaction *transaction,
        return update;
 }
 
-void ref_transaction_update(struct ref_transaction *transaction,
-                           const char *refname,
-                           unsigned char *new_sha1, unsigned char *old_sha1,
-                           int flags, int have_old)
+int ref_transaction_update(struct ref_transaction *transaction,
+                          const char *refname,
+                          const unsigned char *new_sha1,
+                          const unsigned char *old_sha1,
+                          int flags, int have_old,
+                          struct strbuf *err)
 {
-       struct ref_update *update = add_update(transaction, refname);
+       struct ref_update *update;
 
+       if (have_old && !old_sha1)
+               die("BUG: have_old is true but old_sha1 is NULL");
+
+       update = add_update(transaction, refname);
        hashcpy(update->new_sha1, new_sha1);
        update->flags = flags;
        update->have_old = have_old;
        if (have_old)
                hashcpy(update->old_sha1, old_sha1);
+       return 0;
 }
 
 void ref_transaction_create(struct ref_transaction *transaction,
                            const char *refname,
-                           unsigned char *new_sha1,
+                           const unsigned char *new_sha1,
                            int flags)
 {
        struct ref_update *update = add_update(transaction, refname);
@@ -3591,7 +3464,7 @@ void ref_transaction_create(struct ref_transaction *transaction,
 
 void ref_transaction_delete(struct ref_transaction *transaction,
                            const char *refname,
-                           unsigned char *old_sha1,
+                           const unsigned char *old_sha1,
                            int flags, int have_old)
 {
        struct ref_update *update = add_update(transaction, refname);
@@ -3612,7 +3485,7 @@ int update_ref(const char *action, const char *refname,
        lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
        if (!lock)
                return 1;
-       return update_ref_write(action, refname, sha1, lock, onerr);
+       return update_ref_write(action, refname, sha1, lock, NULL, onerr);
 }
 
 static int ref_update_compare(const void *r1, const void *r2)
@@ -3623,28 +3496,23 @@ static int ref_update_compare(const void *r1, const void *r2)
 }
 
 static int ref_update_reject_duplicates(struct ref_update **updates, int n,
-                                       enum action_on_err onerr)
+                                       struct strbuf *err)
 {
        int i;
        for (i = 1; i < n; i++)
                if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
                        const char *str =
                                "Multiple updates for ref '%s' not allowed.";
-                       switch (onerr) {
-                       case UPDATE_REFS_MSG_ON_ERR:
-                               error(str, updates[i]->refname); break;
-                       case UPDATE_REFS_DIE_ON_ERR:
-                               die(str, updates[i]->refname); break;
-                       case UPDATE_REFS_QUIET_ON_ERR:
-                               break;
-                       }
+                       if (err)
+                               strbuf_addf(err, str, updates[i]->refname);
+
                        return 1;
                }
        return 0;
 }
 
 int ref_transaction_commit(struct ref_transaction *transaction,
-                          const char *msg, enum action_on_err onerr)
+                          const char *msg, struct strbuf *err)
 {
        int ret = 0, delnum = 0, i;
        const char **delnames;
@@ -3659,7 +3527,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 
        /* Copy, sort, and reject duplicate refs */
        qsort(updates, n, sizeof(*updates), ref_update_compare);
-       ret = ref_update_reject_duplicates(updates, n, onerr);
+       ret = ref_update_reject_duplicates(updates, n, err);
        if (ret)
                goto cleanup;
 
@@ -3671,8 +3539,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                                               (update->have_old ?
                                                update->old_sha1 : NULL),
                                               update->flags,
-                                              &update->type, onerr);
+                                              &update->type,
+                                              UPDATE_REFS_QUIET_ON_ERR);
                if (!update->lock) {
+                       if (err)
+                               strbuf_addf(err, "Cannot lock the ref '%s'.",
+                                           update->refname);
                        ret = 1;
                        goto cleanup;
                }
@@ -3686,7 +3558,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                        ret = update_ref_write(msg,
                                               update->refname,
                                               update->new_sha1,
-                                              update->lock, onerr);
+                                              update->lock, err,
+                                              UPDATE_REFS_QUIET_ON_ERR);
                        update->lock = NULL; /* freed by update_ref_write */
                        if (ret)
                                goto cleanup;
@@ -3703,7 +3576,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                }
        }
 
-       ret |= repack_without_refs(delnames, delnum);
+       ret |= repack_without_refs(delnames, delnum, err);
        for (i = 0; i < delnum; i++)
                unlink_or_warn(git_path("logs/%s", delnames[i]));
        clear_loose_ref_cache(&ref_cache);
@@ -3713,7 +3586,6 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                if (updates[i]->lock)
                        unlock_ref(updates[i]->lock);
        free(delnames);
-       ref_transaction_free(transaction);
        return ret;
 }
 
diff --git a/refs.h b/refs.h
index 4e3050d976dcdae2e98a50b75e065ea086b9cb25..ec46acdde7b67d3531a4c0e18d33901d2a4f307a 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -82,6 +82,7 @@ extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct st
 /*
  * Lock the packed-refs file for writing.  Flags is passed to
  * hold_lock_file_for_update().  Return 0 on success.
+ * Errno is set to something meaningful on error.
  */
 extern int lock_packed_refs(int flags);
 
@@ -97,6 +98,7 @@ extern void add_packed_ref(const char *refname, const unsigned char *sha1);
  * Write the current version of the packed refs cache from memory to
  * disk.  The packed-refs file must already be locked for writing (see
  * lock_packed_refs()).  Return zero on success.
+ * Sets errno to something meaningful on error.
  */
 extern int commit_packed_refs(void);
 
@@ -121,10 +123,13 @@ extern void rollback_packed_refs(void);
  */
 int pack_refs(unsigned int flags);
 
-extern int repack_without_refs(const char **refnames, int n);
+extern int repack_without_refs(const char **refnames, int n,
+                              struct strbuf *err);
 
 extern int ref_exists(const char *);
 
+extern int is_branch(const char *refname);
+
 /*
  * If refname is a non-symbolic reference that refers to a tag object,
  * and the tag can be (recursively) dereferenced to a non-tag object,
@@ -135,11 +140,15 @@ extern int ref_exists(const char *);
  */
 extern int peel_ref(const char *refname, unsigned char *sha1);
 
-/** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
+/*
+ * Locks a "refs/" ref returning the lock on success and NULL on failure.
+ * On failure errno is set to something meaningful.
+ */
 extern struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1);
 
 /** Locks any ref (for 'HEAD' type refs). */
 #define REF_NODEREF    0x01
+/* errno is set to something meaningful on failure */
 extern struct ref_lock *lock_any_ref_for_update(const char *refname,
                                                const unsigned char *old_sha1,
                                                int flags, int *type_p);
@@ -156,7 +165,9 @@ extern void unlock_ref(struct ref_lock *lock);
 /** Writes sha1 into the ref specified by the lock. **/
 extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
 
-/** Setup reflog before using. **/
+/*
+ * Setup reflog before using. Set errno to something meaningful on failure.
+ */
 int log_ref_setup(const char *refname, char *logfile, int bufsize);
 
 /** Reads log for the value of ref during at_time. **/
@@ -219,17 +230,10 @@ enum action_on_err {
 
 /*
  * Begin a reference transaction.  The reference transaction must
- * eventually be commited using ref_transaction_commit() or rolled
- * back using ref_transaction_rollback().
+ * be freed by calling ref_transaction_free().
  */
 struct ref_transaction *ref_transaction_begin(void);
 
-/*
- * Roll back a ref_transaction and free all associated data.
- */
-void ref_transaction_rollback(struct ref_transaction *transaction);
-
-
 /*
  * The following functions add a reference check or update to a
  * ref_transaction.  In all of them, refname is the name of the
@@ -238,18 +242,22 @@ void ref_transaction_rollback(struct ref_transaction *transaction);
  * can be REF_NODEREF; it is passed to update_ref_lock().
  */
 
-
 /*
  * Add a reference update to transaction.  new_sha1 is the value that
  * the reference should have after the update, or zeros if it should
  * be deleted.  If have_old is true, then old_sha1 holds the value
  * that the reference should have had before the update, or zeros if
  * it must not have existed beforehand.
+ * Function returns 0 on success and non-zero on failure. A failure to update
+ * means that the transaction as a whole has failed and will need to be
+ * rolled back. On failure the err buffer will be updated.
  */
-void ref_transaction_update(struct ref_transaction *transaction,
-                           const char *refname,
-                           unsigned char *new_sha1, unsigned char *old_sha1,
-                           int flags, int have_old);
+int ref_transaction_update(struct ref_transaction *transaction,
+                          const char *refname,
+                          const unsigned char *new_sha1,
+                          const unsigned char *old_sha1,
+                          int flags, int have_old,
+                          struct strbuf *err);
 
 /*
  * Add a reference creation to transaction.  new_sha1 is the value
@@ -259,7 +267,7 @@ void ref_transaction_update(struct ref_transaction *transaction,
  */
 void ref_transaction_create(struct ref_transaction *transaction,
                            const char *refname,
-                           unsigned char *new_sha1,
+                           const unsigned char *new_sha1,
                            int flags);
 
 /*
@@ -269,16 +277,23 @@ void ref_transaction_create(struct ref_transaction *transaction,
  */
 void ref_transaction_delete(struct ref_transaction *transaction,
                            const char *refname,
-                           unsigned char *old_sha1,
+                           const unsigned char *old_sha1,
                            int flags, int have_old);
 
 /*
  * Commit all of the changes that have been queued in transaction, as
  * atomically as possible.  Return a nonzero value if there is a
- * problem.  The ref_transaction is freed by this function.
+ * problem.
+ * If err is non-NULL we will add an error string to it to explain why
+ * the transaction failed. The string does not end in newline.
  */
 int ref_transaction_commit(struct ref_transaction *transaction,
-                          const char *msg, enum action_on_err onerr);
+                          const char *msg, struct strbuf *err);
+
+/*
+ * Free an existing transaction and all associated data.
+ */
+void ref_transaction_free(struct ref_transaction *transaction);
 
 /** Lock a ref and then write its file */
 int update_ref(const char *action, const char *refname,
index cdcca2903b100c775d4e004312086581f5839ad4..0fcf2ce5ff20cc7c6f1bdf6257c94cf8fa21b35a 100644 (file)
@@ -399,7 +399,7 @@ static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
                        rpc->pos = 0;
                        return CURLIOE_OK;
                }
-               fprintf(stderr, "Unable to rewind rpc post data - try increasing http.postBuffer\n");
+               error("unable to rewind rpc post data - try increasing http.postBuffer");
                return CURLIOE_FAILRESTART;
 
        default:
@@ -709,7 +709,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
                free(targets[i]);
        free(targets);
 
-       return ret ? error("Fetch failed.") : 0;
+       return ret ? error("fetch failed.") : 0;
 }
 
 static int fetch_git(struct discovery *heads,
@@ -949,7 +949,7 @@ int main(int argc, const char **argv)
        git_extract_argv0_path(argv[0]);
        setup_git_directory_gently(&nongit);
        if (argc < 2) {
-               fprintf(stderr, "Remote needed\n");
+               error("remote-curl: usage: git remote-curl <remote> [<url>]");
                return 1;
        }
 
@@ -972,16 +972,14 @@ int main(int argc, const char **argv)
 
                if (strbuf_getline(&buf, stdin, '\n') == EOF) {
                        if (ferror(stdin))
-                               fprintf(stderr, "Error reading command stream\n");
-                       else
-                               fprintf(stderr, "Unexpected end of command stream\n");
+                               error("remote-curl: error reading command stream from git");
                        return 1;
                }
                if (buf.len == 0)
                        break;
                if (starts_with(buf.buf, "fetch ")) {
                        if (nongit)
-                               die("Fetch attempted without a local repo");
+                               die("remote-curl: fetch attempted without a local repo");
                        parse_fetch(&buf);
 
                } else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
@@ -1017,7 +1015,7 @@ int main(int argc, const char **argv)
                        printf("\n");
                        fflush(stdout);
                } else {
-                       fprintf(stderr, "Unknown command '%s'\n", buf.buf);
+                       error("remote-curl: unknown command '%s' from git", buf.buf);
                        return 1;
                }
                strbuf_reset(&buf);
index 6be55cbe9ed6cf4979ac0971c30f127c9667ab2e..686e07d317bf157461f0ce680958a466675c80bc 100644 (file)
@@ -175,8 +175,8 @@ static int cmd_import(const char *line)
        char *note_msg;
        unsigned char head_sha1[20];
        unsigned int startrev;
-       struct argv_array svndump_argv = ARGV_ARRAY_INIT;
        struct child_process svndump_proc;
+       const char *command = "svnrdump";
 
        if (read_ref(private_ref, head_sha1))
                startrev = 0;
@@ -202,15 +202,14 @@ static int cmd_import(const char *line)
        } else {
                memset(&svndump_proc, 0, sizeof(struct child_process));
                svndump_proc.out = -1;
-               argv_array_push(&svndump_argv, "svnrdump");
-               argv_array_push(&svndump_argv, "dump");
-               argv_array_push(&svndump_argv, url);
-               argv_array_pushf(&svndump_argv, "-r%u:HEAD", startrev);
-               svndump_proc.argv = svndump_argv.argv;
+               argv_array_push(&svndump_proc.args, command);
+               argv_array_push(&svndump_proc.args, "dump");
+               argv_array_push(&svndump_proc.args, url);
+               argv_array_pushf(&svndump_proc.args, "-r%u:HEAD", startrev);
 
                code = start_command(&svndump_proc);
                if (code)
-                       die("Unable to start %s, code %d", svndump_proc.argv[0], code);
+                       die("Unable to start %s, code %d", command, code);
                dumpin_fd = svndump_proc.out;
        }
        /* setup marks file import/export */
@@ -226,8 +225,7 @@ static int cmd_import(const char *line)
        if (!dump_from_file) {
                code = finish_command(&svndump_proc);
                if (code)
-                       warning("%s, returned %d", svndump_proc.argv[0], code);
-               argv_array_clear(&svndump_argv);
+                       warning("%s, returned %d", command, code);
        }
 
        return 0;
index d55aa8a01b4a4f200f24972795f2887644a06d43..d84b495895bc6d42980c388ed03c560174604f92 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -207,11 +207,11 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
                        strbuf_reset(&one);
                        strbuf_reset(&two);
                } else if (hunk == RR_SIDE_1)
-                       strbuf_addstr(&one, buf.buf);
+                       strbuf_addbuf(&one, &buf);
                else if (hunk == RR_ORIGINAL)
                        ; /* discard */
                else if (hunk == RR_SIDE_2)
-                       strbuf_addstr(&two, buf.buf);
+                       strbuf_addbuf(&two, &buf);
                else
                        rerere_io_putstr(buf.buf, io);
                continue;
@@ -492,8 +492,7 @@ static int update_paths(struct string_list *update)
        }
 
        if (!status && active_cache_changed) {
-               if (write_cache(fd, active_cache, active_nr) ||
-                   commit_locked_index(&index_lock))
+               if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                        die("Unable to write new index file");
        } else if (fd >= 0)
                rollback_lock_file(&index_lock);
index 44c697c36d0b406330aeb084db1d6a4be715d6c6..468a2eb92c6d72c519faf5c8203d3d41d520c12d 100644 (file)
@@ -110,7 +110,7 @@ void resolve_undo_clear_index(struct index_state *istate)
        string_list_clear(resolve_undo, 1);
        free(resolve_undo);
        istate->resolve_undo = NULL;
-       istate->cache_changed = 1;
+       istate->cache_changed |= RESOLVE_UNDO_CHANGED;
 }
 
 int unmerge_index_entry_at(struct index_state *istate, int pos)
index 82dd0853a195adc000cd88c9b5874969f3836df0..35a3ebf07b1792ac7b0ecef30781223591413093 100644 (file)
@@ -764,28 +764,21 @@ char *find_hook(const char *name)
 int run_hook_ve(const char *const *env, const char *name, va_list args)
 {
        struct child_process hook;
-       struct argv_array argv = ARGV_ARRAY_INIT;
        const char *p;
-       int ret;
 
        p = find_hook(name);
        if (!p)
                return 0;
 
-       argv_array_push(&argv, p);
-
-       while ((p = va_arg(args, const char *)))
-               argv_array_push(&argv, p);
-
        memset(&hook, 0, sizeof(hook));
-       hook.argv = argv.argv;
+       argv_array_push(&hook.args, p);
+       while ((p = va_arg(args, const char *)))
+               argv_array_push(&hook.args, p);
        hook.env = env;
        hook.no_stdin = 1;
        hook.stdout_to_stderr = 1;
 
-       ret = run_command(&hook);
-       argv_array_clear(&argv);
-       return ret;
+       return run_command(&hook);
 }
 
 int run_hook_le(const char *const *env, const char *name, ...)
index cdd30c07379621561edf7b7fb23c5607571edf4f..3c060e054720b7b17f9a868cabb1a0ef62aa1b50 100644 (file)
@@ -263,11 +263,11 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 {
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
-       int clean, index_fd;
+       int clean;
        const char **xopt;
        static struct lock_file index_lock;
 
-       index_fd = hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, 1);
 
        read_cache();
 
@@ -288,8 +288,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
                            next_tree, base_tree, &result);
 
        if (active_cache_changed &&
-           (write_cache(index_fd, active_cache, active_nr) ||
-            commit_locked_index(&index_lock)))
+           write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
                die(_("%s: Unable to write new index file"), action_name(opts));
        rollback_lock_file(&index_lock);
@@ -341,9 +340,7 @@ static int is_index_unchanged(void)
                active_cache_tree = cache_tree();
 
        if (!cache_tree_fully_valid(active_cache_tree))
-               if (cache_tree_update(active_cache_tree,
-                                     (const struct cache_entry * const *)active_cache,
-                                     active_nr, 0))
+               if (cache_tree_update(&the_index, 0))
                        return error(_("Unable to update cache tree\n"));
 
        return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
@@ -643,9 +640,8 @@ static void read_and_refresh_cache(struct replay_opts *opts)
        if (read_index_preload(&the_index, NULL) < 0)
                die(_("git %s: failed to read the index"), action_name(opts));
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
-       if (the_index.cache_changed) {
-               if (write_index(&the_index, index_fd) ||
-                   commit_locked_index(&index_lock))
+       if (the_index.cache_changed && index_fd >= 0) {
+               if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                        die(_("git %s: failed to refresh the index"), action_name(opts));
        }
        rollback_lock_file(&index_lock);
index 855d28cf9440aba175489b63e5d24665126faf2a..6dd03a974ae9a5063c40cd50e0c6e7645afca971 100644 (file)
@@ -278,9 +278,7 @@ static string_list_ty variables_set;
 static void
 note_variable (const char *var_ptr, size_t var_len)
 {
-  char *string = xmalloc (var_len + 1);
-  memcpy (string, var_ptr, var_len);
-  string[var_len] = '\0';
+  char *string = xmemdupz (var_ptr, var_len);
 
   string_list_append (&variables_set, string);
 }
index a38854ce553c1e59294d5542a37a5404ccd66dc5..3f70b1d86aaa8e0648d8a3c6ebb6aca701ae7475 100644 (file)
@@ -36,9 +36,6 @@ static inline uintmax_t sz_fmt(size_t s) { return s; }
 
 const unsigned char null_sha1[20];
 
-static const char *no_log_pack_access = "no_log_pack_access";
-static const char *log_pack_access;
-
 /*
  * This is meant to hold a *small* number of objects that you would
  * want read_sha1_file() to be able to return, but yet you do not want
@@ -268,9 +265,9 @@ static struct alternate_object_database **alt_odb_tail;
  * SHA1, an extra slash for the first level indirection, and the
  * terminating NUL.
  */
-static int link_alt_odb_entry(const char *entry, const char *relative_base, int depth)
+static int link_alt_odb_entry(const char *entry, const char *relative_base,
+       int depth, const char *normalized_objdir)
 {
-       const char *objdir = get_object_directory();
        struct alternate_object_database *ent;
        struct alternate_object_database *alt;
        int pfxlen, entlen;
@@ -321,7 +318,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base, int
                        return -1;
                }
        }
-       if (!strcmp(ent->base, objdir)) {
+       if (!strcmp_icase(ent->base, normalized_objdir)) {
                free(ent);
                return -1;
        }
@@ -345,6 +342,7 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
        struct string_list entries = STRING_LIST_INIT_NODUP;
        char *alt_copy;
        int i;
+       struct strbuf objdirbuf = STRBUF_INIT;
 
        if (depth > 5) {
                error("%s: ignoring alternate object stores, nesting too deep.",
@@ -352,6 +350,9 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
                return;
        }
 
+       strbuf_addstr(&objdirbuf, absolute_path(get_object_directory()));
+       normalize_path_copy(objdirbuf.buf, objdirbuf.buf);
+
        alt_copy = xmemdupz(alt, len);
        string_list_split_in_place(&entries, alt_copy, sep, -1);
        for (i = 0; i < entries.nr; i++) {
@@ -362,11 +363,12 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
                        error("%s: ignoring relative alternate object store %s",
                                        relative_base, entry);
                } else {
-                       link_alt_odb_entry(entry, relative_base, depth);
+                       link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf);
                }
        }
        string_list_clear(&entries, 0);
        free(alt_copy);
+       strbuf_release(&objdirbuf);
 }
 
 void read_info_alternates(const char * relative_base, int depth)
@@ -1178,48 +1180,42 @@ static void report_pack_garbage(struct string_list *list)
 
 static void prepare_packed_git_one(char *objdir, int local)
 {
-       /* Ensure that this buffer is large enough so that we can
-          append "/pack/" without clobbering the stack even if
-          strlen(objdir) were PATH_MAX.  */
-       char path[PATH_MAX + 1 + 4 + 1 + 1];
-       int len;
+       struct strbuf path = STRBUF_INIT;
+       size_t dirnamelen;
        DIR *dir;
        struct dirent *de;
        struct string_list garbage = STRING_LIST_INIT_DUP;
 
-       sprintf(path, "%s/pack", objdir);
-       len = strlen(path);
-       dir = opendir(path);
+       strbuf_addstr(&path, objdir);
+       strbuf_addstr(&path, "/pack");
+       dir = opendir(path.buf);
        if (!dir) {
                if (errno != ENOENT)
                        error("unable to open object pack directory: %s: %s",
-                             path, strerror(errno));
+                             path.buf, strerror(errno));
+               strbuf_release(&path);
                return;
        }
-       path[len++] = '/';
+       strbuf_addch(&path, '/');
+       dirnamelen = path.len;
        while ((de = readdir(dir)) != NULL) {
-               int namelen = strlen(de->d_name);
                struct packed_git *p;
-
-               if (len + namelen + 1 > sizeof(path)) {
-                       if (report_garbage) {
-                               struct strbuf sb = STRBUF_INIT;
-                               strbuf_addf(&sb, "%.*s/%s", len - 1, path, de->d_name);
-                               report_garbage("path too long", sb.buf);
-                               strbuf_release(&sb);
-                       }
-                       continue;
-               }
+               size_t base_len;
 
                if (is_dot_or_dotdot(de->d_name))
                        continue;
 
-               strcpy(path + len, de->d_name);
+               strbuf_setlen(&path, dirnamelen);
+               strbuf_addstr(&path, de->d_name);
 
-               if (has_extension(de->d_name, ".idx")) {
+               base_len = path.len;
+               if (strip_suffix_mem(path.buf, &base_len, ".idx")) {
                        /* Don't reopen a pack we already have. */
                        for (p = packed_git; p; p = p->next) {
-                               if (!memcmp(path, p->pack_name, len + namelen - 4))
+                               size_t len;
+                               if (strip_suffix(p->pack_name, ".pack", &len) &&
+                                   len == base_len &&
+                                   !memcmp(p->pack_name, path.buf, len))
                                        break;
                        }
                        if (p == NULL &&
@@ -1227,24 +1223,25 @@ static void prepare_packed_git_one(char *objdir, int local)
                             * See if it really is a valid .idx file with
                             * corresponding .pack file that we can map.
                             */
-                           (p = add_packed_git(path, len + namelen, local)) != NULL)
+                           (p = add_packed_git(path.buf, path.len, local)) != NULL)
                                install_packed_git(p);
                }
 
                if (!report_garbage)
                        continue;
 
-               if (has_extension(de->d_name, ".idx") ||
-                   has_extension(de->d_name, ".pack") ||
-                   has_extension(de->d_name, ".bitmap") ||
-                   has_extension(de->d_name, ".keep"))
-                       string_list_append(&garbage, path);
+               if (ends_with(de->d_name, ".idx") ||
+                   ends_with(de->d_name, ".pack") ||
+                   ends_with(de->d_name, ".bitmap") ||
+                   ends_with(de->d_name, ".keep"))
+                       string_list_append(&garbage, path.buf);
                else
-                       report_garbage("garbage found", path);
+                       report_garbage("garbage found", path.buf);
        }
        closedir(dir);
        report_pack_garbage(&garbage);
        string_list_clear(&garbage, 0);
+       strbuf_release(&path);
 }
 
 static int sort_pack(const void *a_, const void *b_)
@@ -2086,27 +2083,9 @@ static void *read_object(const unsigned char *sha1, enum object_type *type,
 
 static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
 {
-       static FILE *log_file;
-
-       if (!log_pack_access)
-               log_pack_access = getenv("GIT_TRACE_PACK_ACCESS");
-       if (!log_pack_access)
-               log_pack_access = no_log_pack_access;
-       if (log_pack_access == no_log_pack_access)
-               return;
-
-       if (!log_file) {
-               log_file = fopen(log_pack_access, "w");
-               if (!log_file) {
-                       error("cannot open pack access log '%s' for writing: %s",
-                             log_pack_access, strerror(errno));
-                       log_pack_access = no_log_pack_access;
-                       return;
-               }
-       }
-       fprintf(log_file, "%s %"PRIuMAX"\n",
-               p->pack_name, (uintmax_t)obj_offset);
-       fflush(log_file);
+       static struct trace_key pack_access = TRACE_KEY_INIT(PACK_ACCESS);
+       trace_printf_key(&pack_access, "%s %"PRIuMAX"\n",
+                        p->pack_name, (uintmax_t)obj_offset);
 }
 
 int do_check_packed_object_crc;
@@ -2131,8 +2110,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
        int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
        int base_from_cache = 0;
 
-       if (log_pack_access != no_log_pack_access)
-               write_pack_access_log(p, obj_offset);
+       write_pack_access_log(p, obj_offset);
 
        /* PHASE 1: drill down to the innermost base object */
        for (;;) {
index 5bfa8416999d35d6bb5c615d86763f013b7df748..63ee66fedd617e2cacfae7a763f43ee600d15273 100644 (file)
@@ -540,8 +540,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                        char *tmp = xstrndup(str + at + 2, reflog_len);
                        at_time = approxidate_careful(tmp, &errors);
                        free(tmp);
-                       if (errors)
+                       if (errors) {
+                               free(real_ref);
                                return -1;
+                       }
                }
                if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
                                &co_time, &co_tz, &co_cnt)) {
@@ -946,7 +948,7 @@ static int interpret_nth_prior_checkout(const char *name, int namelen,
        retval = 0;
        if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) {
                strbuf_reset(buf);
-               strbuf_add(buf, cb.buf.buf, cb.buf.len);
+               strbuf_addbuf(buf, &cb.buf);
                retval = brace - name + 1;
        }
 
index 0b267b64117c5f1d2df662e3d413418d575a94bd..de07709e3358947062984970e85005ded224baf9 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -325,7 +325,7 @@ void prune_shallow(int show_only)
        strbuf_release(&sb);
 }
 
-#define TRACE_KEY "GIT_TRACE_SHALLOW"
+struct trace_key trace_shallow = TRACE_KEY_INIT(SHALLOW);
 
 /*
  * Step 1, split sender shallow commits into "ours" and "theirs"
@@ -334,7 +334,7 @@ void prune_shallow(int show_only)
 void prepare_shallow_info(struct shallow_info *info, struct sha1_array *sa)
 {
        int i;
-       trace_printf_key(TRACE_KEY, "shallow: prepare_shallow_info\n");
+       trace_printf_key(&trace_shallow, "shallow: prepare_shallow_info\n");
        memset(info, 0, sizeof(*info));
        info->shallow = sa;
        if (!sa)
@@ -365,7 +365,7 @@ void remove_nonexistent_theirs_shallow(struct shallow_info *info)
 {
        unsigned char (*sha1)[20] = info->shallow->sha1;
        int i, dst;
-       trace_printf_key(TRACE_KEY, "shallow: remove_nonexistent_theirs_shallow\n");
+       trace_printf_key(&trace_shallow, "shallow: remove_nonexistent_theirs_shallow\n");
        for (i = dst = 0; i < info->nr_theirs; i++) {
                if (i != dst)
                        info->theirs[dst] = info->theirs[i];
@@ -516,7 +516,7 @@ void assign_shallow_commits_to_refs(struct shallow_info *info,
        int *shallow, nr_shallow = 0;
        struct paint_info pi;
 
-       trace_printf_key(TRACE_KEY, "shallow: assign_shallow_commits_to_refs\n");
+       trace_printf_key(&trace_shallow, "shallow: assign_shallow_commits_to_refs\n");
        shallow = xmalloc(sizeof(*shallow) * (info->nr_ours + info->nr_theirs));
        for (i = 0; i < info->nr_ours; i++)
                shallow[nr_shallow++] = info->ours[i];
@@ -622,7 +622,7 @@ static void post_assign_shallow(struct shallow_info *info,
        int bitmap_nr = (info->ref->nr + 31) / 32;
        struct commit_array ca;
 
-       trace_printf_key(TRACE_KEY, "shallow: post_assign_shallow\n");
+       trace_printf_key(&trace_shallow, "shallow: post_assign_shallow\n");
        if (ref_status)
                memset(ref_status, 0, sizeof(*ref_status) * info->ref->nr);
 
diff --git a/split-index.c b/split-index.c
new file mode 100644 (file)
index 0000000..21485e2
--- /dev/null
@@ -0,0 +1,328 @@
+#include "cache.h"
+#include "split-index.h"
+#include "ewah/ewok.h"
+
+struct split_index *init_split_index(struct index_state *istate)
+{
+       if (!istate->split_index) {
+               istate->split_index = xcalloc(1, sizeof(*istate->split_index));
+               istate->split_index->refcount = 1;
+       }
+       return istate->split_index;
+}
+
+int read_link_extension(struct index_state *istate,
+                        const void *data_, unsigned long sz)
+{
+       const unsigned char *data = data_;
+       struct split_index *si;
+       int ret;
+
+       if (sz < 20)
+               return error("corrupt link extension (too short)");
+       si = init_split_index(istate);
+       hashcpy(si->base_sha1, data);
+       data += 20;
+       sz -= 20;
+       if (!sz)
+               return 0;
+       si->delete_bitmap = ewah_new();
+       ret = ewah_read_mmap(si->delete_bitmap, data, sz);
+       if (ret < 0)
+               return error("corrupt delete bitmap in link extension");
+       data += ret;
+       sz -= ret;
+       si->replace_bitmap = ewah_new();
+       ret = ewah_read_mmap(si->replace_bitmap, data, sz);
+       if (ret < 0)
+               return error("corrupt replace bitmap in link extension");
+       if (ret != sz)
+               return error("garbage at the end of link extension");
+       return 0;
+}
+
+static int write_strbuf(void *user_data, const void *data, size_t len)
+{
+       struct strbuf *sb = user_data;
+       strbuf_add(sb, data, len);
+       return len;
+}
+
+int write_link_extension(struct strbuf *sb,
+                        struct index_state *istate)
+{
+       struct split_index *si = istate->split_index;
+       strbuf_add(sb, si->base_sha1, 20);
+       if (!si->delete_bitmap && !si->replace_bitmap)
+               return 0;
+       ewah_serialize_to(si->delete_bitmap, write_strbuf, sb);
+       ewah_serialize_to(si->replace_bitmap, write_strbuf, sb);
+       return 0;
+}
+
+static void mark_base_index_entries(struct index_state *base)
+{
+       int i;
+       /*
+        * To keep track of the shared entries between
+        * istate->base->cache[] and istate->cache[], base entry
+        * position is stored in each base entry. All positions start
+        * from 1 instead of 0, which is resrved to say "this is a new
+        * entry".
+        */
+       for (i = 0; i < base->cache_nr; i++)
+               base->cache[i]->index = i + 1;
+}
+
+void move_cache_to_base_index(struct index_state *istate)
+{
+       struct split_index *si = istate->split_index;
+       int i;
+
+       /*
+        * do not delete old si->base, its index entries may be shared
+        * with istate->cache[]. Accept a bit of leaking here because
+        * this code is only used by short-lived update-index.
+        */
+       si->base = xcalloc(1, sizeof(*si->base));
+       si->base->version = istate->version;
+       /* zero timestamp disables racy test in ce_write_index() */
+       si->base->timestamp = istate->timestamp;
+       ALLOC_GROW(si->base->cache, istate->cache_nr, si->base->cache_alloc);
+       si->base->cache_nr = istate->cache_nr;
+       memcpy(si->base->cache, istate->cache,
+              sizeof(*istate->cache) * istate->cache_nr);
+       mark_base_index_entries(si->base);
+       for (i = 0; i < si->base->cache_nr; i++)
+               si->base->cache[i]->ce_flags &= ~CE_UPDATE_IN_BASE;
+}
+
+static void mark_entry_for_delete(size_t pos, void *data)
+{
+       struct index_state *istate = data;
+       if (pos >= istate->cache_nr)
+               die("position for delete %d exceeds base index size %d",
+                   (int)pos, istate->cache_nr);
+       istate->cache[pos]->ce_flags |= CE_REMOVE;
+       istate->split_index->nr_deletions = 1;
+}
+
+static void replace_entry(size_t pos, void *data)
+{
+       struct index_state *istate = data;
+       struct split_index *si = istate->split_index;
+       struct cache_entry *dst, *src;
+
+       if (pos >= istate->cache_nr)
+               die("position for replacement %d exceeds base index size %d",
+                   (int)pos, istate->cache_nr);
+       if (si->nr_replacements >= si->saved_cache_nr)
+               die("too many replacements (%d vs %d)",
+                   si->nr_replacements, si->saved_cache_nr);
+       dst = istate->cache[pos];
+       if (dst->ce_flags & CE_REMOVE)
+               die("entry %d is marked as both replaced and deleted",
+                   (int)pos);
+       src = si->saved_cache[si->nr_replacements];
+       if (ce_namelen(src))
+               die("corrupt link extension, entry %d should have "
+                   "zero length name", (int)pos);
+       src->index = pos + 1;
+       src->ce_flags |= CE_UPDATE_IN_BASE;
+       src->ce_namelen = dst->ce_namelen;
+       copy_cache_entry(dst, src);
+       free(src);
+       si->nr_replacements++;
+}
+
+void merge_base_index(struct index_state *istate)
+{
+       struct split_index *si = istate->split_index;
+       unsigned int i;
+
+       mark_base_index_entries(si->base);
+
+       si->saved_cache     = istate->cache;
+       si->saved_cache_nr  = istate->cache_nr;
+       istate->cache_nr    = si->base->cache_nr;
+       istate->cache       = NULL;
+       istate->cache_alloc = 0;
+       ALLOC_GROW(istate->cache, istate->cache_nr, istate->cache_alloc);
+       memcpy(istate->cache, si->base->cache,
+              sizeof(*istate->cache) * istate->cache_nr);
+
+       si->nr_deletions = 0;
+       si->nr_replacements = 0;
+       ewah_each_bit(si->replace_bitmap, replace_entry, istate);
+       ewah_each_bit(si->delete_bitmap, mark_entry_for_delete, istate);
+       if (si->nr_deletions)
+               remove_marked_cache_entries(istate);
+
+       for (i = si->nr_replacements; i < si->saved_cache_nr; i++) {
+               if (!ce_namelen(si->saved_cache[i]))
+                       die("corrupt link extension, entry %d should "
+                           "have non-zero length name", i);
+               add_index_entry(istate, si->saved_cache[i],
+                               ADD_CACHE_OK_TO_ADD |
+                               ADD_CACHE_KEEP_CACHE_TREE |
+                               /*
+                                * we may have to replay what
+                                * merge-recursive.c:update_stages()
+                                * does, which has this flag on
+                                */
+                               ADD_CACHE_SKIP_DFCHECK);
+               si->saved_cache[i] = NULL;
+       }
+
+       ewah_free(si->delete_bitmap);
+       ewah_free(si->replace_bitmap);
+       free(si->saved_cache);
+       si->delete_bitmap  = NULL;
+       si->replace_bitmap = NULL;
+       si->saved_cache    = NULL;
+       si->saved_cache_nr = 0;
+}
+
+void prepare_to_write_split_index(struct index_state *istate)
+{
+       struct split_index *si = init_split_index(istate);
+       struct cache_entry **entries = NULL, *ce;
+       int i, nr_entries = 0, nr_alloc = 0;
+
+       si->delete_bitmap = ewah_new();
+       si->replace_bitmap = ewah_new();
+
+       if (si->base) {
+               /* Go through istate->cache[] and mark CE_MATCHED to
+                * entry with positive index. We'll go through
+                * base->cache[] later to delete all entries in base
+                * that are not marked eith either CE_MATCHED or
+                * CE_UPDATE_IN_BASE. If istate->cache[i] is a
+                * duplicate, deduplicate it.
+                */
+               for (i = 0; i < istate->cache_nr; i++) {
+                       struct cache_entry *base;
+                       /* namelen is checked separately */
+                       const unsigned int ondisk_flags =
+                               CE_STAGEMASK | CE_VALID | CE_EXTENDED_FLAGS;
+                       unsigned int ce_flags, base_flags, ret;
+                       ce = istate->cache[i];
+                       if (!ce->index)
+                               continue;
+                       if (ce->index > si->base->cache_nr) {
+                               ce->index = 0;
+                               continue;
+                       }
+                       ce->ce_flags |= CE_MATCHED; /* or "shared" */
+                       base = si->base->cache[ce->index - 1];
+                       if (ce == base)
+                               continue;
+                       if (ce->ce_namelen != base->ce_namelen ||
+                           strcmp(ce->name, base->name)) {
+                               ce->index = 0;
+                               continue;
+                       }
+                       ce_flags = ce->ce_flags;
+                       base_flags = base->ce_flags;
+                       /* only on-disk flags matter */
+                       ce->ce_flags   &= ondisk_flags;
+                       base->ce_flags &= ondisk_flags;
+                       ret = memcmp(&ce->ce_stat_data, &base->ce_stat_data,
+                                    offsetof(struct cache_entry, name) -
+                                    offsetof(struct cache_entry, ce_stat_data));
+                       ce->ce_flags = ce_flags;
+                       base->ce_flags = base_flags;
+                       if (ret)
+                               ce->ce_flags |= CE_UPDATE_IN_BASE;
+                       free(base);
+                       si->base->cache[ce->index - 1] = ce;
+               }
+               for (i = 0; i < si->base->cache_nr; i++) {
+                       ce = si->base->cache[i];
+                       if ((ce->ce_flags & CE_REMOVE) ||
+                           !(ce->ce_flags & CE_MATCHED))
+                               ewah_set(si->delete_bitmap, i);
+                       else if (ce->ce_flags & CE_UPDATE_IN_BASE) {
+                               ewah_set(si->replace_bitmap, i);
+                               ce->ce_flags |= CE_STRIP_NAME;
+                               ALLOC_GROW(entries, nr_entries+1, nr_alloc);
+                               entries[nr_entries++] = ce;
+                       }
+               }
+       }
+
+       for (i = 0; i < istate->cache_nr; i++) {
+               ce = istate->cache[i];
+               if ((!si->base || !ce->index) && !(ce->ce_flags & CE_REMOVE)) {
+                       assert(!(ce->ce_flags & CE_STRIP_NAME));
+                       ALLOC_GROW(entries, nr_entries+1, nr_alloc);
+                       entries[nr_entries++] = ce;
+               }
+               ce->ce_flags &= ~CE_MATCHED;
+       }
+
+       /*
+        * take cache[] out temporarily, put entries[] in its place
+        * for writing
+        */
+       si->saved_cache = istate->cache;
+       si->saved_cache_nr = istate->cache_nr;
+       istate->cache = entries;
+       istate->cache_nr = nr_entries;
+}
+
+void finish_writing_split_index(struct index_state *istate)
+{
+       struct split_index *si = init_split_index(istate);
+
+       ewah_free(si->delete_bitmap);
+       ewah_free(si->replace_bitmap);
+       si->delete_bitmap = NULL;
+       si->replace_bitmap = NULL;
+       free(istate->cache);
+       istate->cache = si->saved_cache;
+       istate->cache_nr = si->saved_cache_nr;
+}
+
+void discard_split_index(struct index_state *istate)
+{
+       struct split_index *si = istate->split_index;
+       if (!si)
+               return;
+       istate->split_index = NULL;
+       si->refcount--;
+       if (si->refcount)
+               return;
+       if (si->base) {
+               discard_index(si->base);
+               free(si->base);
+       }
+       free(si);
+}
+
+void save_or_free_index_entry(struct index_state *istate, struct cache_entry *ce)
+{
+       if (ce->index &&
+           istate->split_index &&
+           istate->split_index->base &&
+           ce->index <= istate->split_index->base->cache_nr &&
+           ce == istate->split_index->base->cache[ce->index - 1])
+               ce->ce_flags |= CE_REMOVE;
+       else
+               free(ce);
+}
+
+void replace_index_entry_in_base(struct index_state *istate,
+                                struct cache_entry *old,
+                                struct cache_entry *new)
+{
+       if (old->index &&
+           istate->split_index &&
+           istate->split_index->base &&
+           old->index <= istate->split_index->base->cache_nr) {
+               new->index = old->index;
+               if (old != istate->split_index->base->cache[new->index - 1])
+                       free(istate->split_index->base->cache[new->index - 1]);
+               istate->split_index->base->cache[new->index - 1] = new;
+       }
+}
diff --git a/split-index.h b/split-index.h
new file mode 100644 (file)
index 0000000..c1324f5
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef SPLIT_INDEX_H
+#define SPLIT_INDEX_H
+
+struct index_state;
+struct strbuf;
+struct ewah_bitmap;
+
+struct split_index {
+       unsigned char base_sha1[20];
+       struct index_state *base;
+       struct ewah_bitmap *delete_bitmap;
+       struct ewah_bitmap *replace_bitmap;
+       struct cache_entry **saved_cache;
+       unsigned int saved_cache_nr;
+       unsigned int nr_deletions;
+       unsigned int nr_replacements;
+       int refcount;
+};
+
+struct split_index *init_split_index(struct index_state *istate);
+void save_or_free_index_entry(struct index_state *istate, struct cache_entry *ce);
+void replace_index_entry_in_base(struct index_state *istate,
+                                struct cache_entry *old,
+                                struct cache_entry *new);
+int read_link_extension(struct index_state *istate,
+                       const void *data, unsigned long sz);
+int write_link_extension(struct strbuf *sb,
+                        struct index_state *istate);
+void move_cache_to_base_index(struct index_state *istate);
+void merge_base_index(struct index_state *istate);
+void prepare_to_write_split_index(struct index_state *istate);
+void finish_writing_split_index(struct index_state *istate);
+void discard_split_index(struct index_state *istate);
+
+#endif
index 12c78656ca9ddb51fb62b95be29d18056911803f..33018d847f08424211c02cff8002680f5a2f02e9 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -11,15 +11,6 @@ int starts_with(const char *str, const char *prefix)
                        return 0;
 }
 
-int ends_with(const char *str, const char *suffix)
-{
-       int len = strlen(str), suflen = strlen(suffix);
-       if (len < suflen)
-               return 0;
-       else
-               return !strcmp(str + len - suflen, suffix);
-}
-
 /*
  * Used as the default ->buf value, so that people can always assume
  * buf is non NULL and ->buf is NUL terminated even for a freshly
index a594c24b2b0e9e82c16de1bb787758d561cb2e7b..a7c0192e9ee392bd232b325692a9111b3e160e84 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -49,6 +49,15 @@ extern int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
 extern void strbuf_tolower(struct strbuf *sb);
 extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
 
+static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
+{
+       if (strip_suffix_mem(sb->buf, &sb->len, suffix)) {
+               strbuf_setlen(sb, sb->len);
+               return 1;
+       } else
+               return 0;
+}
+
 /*
  * Split str (of length slen) at the specified terminator character.
  * Return a null-terminated array of pointers to strbuf objects
index aabb25ef4c1040dde015d6ac37b8213d9bc958ea..db38b62b46fd15d849ed405f0b60cb7c72eaee40 100644 (file)
@@ -1,6 +1,12 @@
 #include "cache.h"
 #include "string-list.h"
 
+void string_list_init(struct string_list *list, int strdup_strings)
+{
+       memset(list, 0, sizeof(*list));
+       list->strdup_strings = strdup_strings;
+}
+
 /* if there is no exact match, point to the index where the entry could be
  * inserted */
 static int get_entry_index(const struct string_list *list, const char *string,
index dd5e2944658cce2428cef34a6f920d63a4bd3628..494eb5d95de0e5e8d69705f7a6f4fe2efcb232ad 100644 (file)
@@ -18,6 +18,8 @@ struct string_list {
 #define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0, NULL }
 #define STRING_LIST_INIT_DUP   { NULL, 0, 0, 1, NULL }
 
+void string_list_init(struct string_list *list, int strdup_strings);
+
 void print_string_list(const struct string_list *p, const char *text);
 void string_list_clear(struct string_list *list, int free_util);
 
index b80ecacf60dc87025336b6b6d6a51acc9321f429..c3a61e70f9f72eced2530d425717972881b947c8 100644 (file)
@@ -544,10 +544,7 @@ static int push_submodule(const char *path)
 int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
 {
        int i, ret = 1;
-       struct string_list needs_pushing;
-
-       memset(&needs_pushing, 0, sizeof(struct string_list));
-       needs_pushing.strdup_strings = 1;
+       struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 
        if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
                return 1;
@@ -965,7 +962,7 @@ static int find_first_merges(struct object_array *result, const char *path,
                        sha1_to_hex(a->object.sha1));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
-       setup_revisions(sizeof(rev_args)/sizeof(char *)-1, rev_args, &revs, &rev_opts);
+       setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
 
        /* save all revisions from the above list that contain b */
        if (prepare_revision_walk(&revs))
index 8fd1a723576c773e4a63d7de7b0ad5cab5a04627..43b15e36aeeba4ddc48073f8463592efc5216e3c 100644 (file)
@@ -13,7 +13,7 @@ TAR ?= $(TAR)
 RM ?= rm -f
 PROVE ?= prove
 DEFAULT_TEST_TARGET ?= test
-TEST_LINT ?= test-lint-duplicates test-lint-executable
+TEST_LINT ?= test-lint
 
 ifdef TEST_OUTPUT_DIRECTORY
 TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
@@ -29,6 +29,7 @@ TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
 T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
 TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh))
 TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
+THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
 
 all: $(DEFAULT_TEST_TARGET)
 
@@ -65,7 +66,7 @@ test-lint-executable:
                echo >&2 "non-executable tests:" $$bad; exit 1; }
 
 test-lint-shell-syntax:
-       @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T)
+       @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T) $(THELPERS)
 
 aggregate-results-and-cleanup: $(T)
        $(MAKE) aggregate-results
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
new file mode 100755 (executable)
index 0000000..79cdd34
--- /dev/null
@@ -0,0 +1,680 @@
+# Create a submodule layout used for all tests below.
+#
+# The following use cases are covered:
+# - New submodule (no_submodule => add_sub1)
+# - Removed submodule (add_sub1 => remove_sub1)
+# - Updated submodule (add_sub1 => modify_sub1)
+# - Submodule updated to invalid commit (add_sub1 => invalid_sub1)
+# - Submodule updated from invalid commit (invalid_sub1 => valid_sub1)
+# - Submodule replaced by tracked files in directory (add_sub1 =>
+#   replace_sub1_with_directory)
+# - Directory containing tracked files replaced by submodule
+#   (replace_sub1_with_directory => replace_directory_with_sub1)
+# - Submodule replaced by tracked file with the same name (add_sub1 =>
+#   replace_sub1_with_file)
+# - Tracked file replaced by submodule (replace_sub1_with_file =>
+#   replace_file_with_sub1)
+#
+#                   --O-----O
+#                  /  ^     replace_directory_with_sub1
+#                 /   replace_sub1_with_directory
+#                /----O
+#               /     ^
+#              /      modify_sub1
+#      O------O-------O
+#      ^      ^\      ^
+#      |      | \     remove_sub1
+#      |      |  -----O-----O
+#      |      |   \   ^     replace_file_with_sub1
+#      |      |    \  replace_sub1_with_file
+#      |   add_sub1 --O-----O
+# no_submodule        ^     valid_sub1
+#                     invalid_sub1
+#
+create_lib_submodule_repo () {
+       git init submodule_update_repo &&
+       (
+               cd submodule_update_repo &&
+               echo "expect" >>.gitignore &&
+               echo "actual" >>.gitignore &&
+               echo "x" >file1 &&
+               echo "y" >file2 &&
+               git add .gitignore file1 file2 &&
+               git commit -m "Base" &&
+               git branch "no_submodule" &&
+
+               git checkout -b "add_sub1" &&
+               git submodule add ./. sub1 &&
+               git config -f .gitmodules submodule.sub1.ignore all &&
+               git config submodule.sub1.ignore all &&
+               git add .gitmodules &&
+               git commit -m "Add sub1" &&
+               git checkout -b remove_sub1 &&
+               git revert HEAD &&
+
+               git checkout -b "modify_sub1" "add_sub1" &&
+               git submodule update &&
+               (
+                       cd sub1 &&
+                       git fetch &&
+                       git checkout -b "modifications" &&
+                       echo "z" >file2 &&
+                       echo "x" >file3 &&
+                       git add file2 file3 &&
+                       git commit -m "modified file2 and added file3" &&
+                       git push origin modifications
+               ) &&
+               git add sub1 &&
+               git commit -m "Modify sub1" &&
+
+               git checkout -b "replace_sub1_with_directory" "add_sub1" &&
+               git submodule update &&
+               (
+                       cd sub1 &&
+                       git checkout modifications
+               ) &&
+               git rm --cached sub1 &&
+               rm sub1/.git* &&
+               git config -f .gitmodules --remove-section "submodule.sub1" &&
+               git add .gitmodules sub1/* &&
+               git commit -m "Replace sub1 with directory" &&
+               git checkout -b replace_directory_with_sub1 &&
+               git revert HEAD &&
+
+               git checkout -b "replace_sub1_with_file" "add_sub1" &&
+               git rm sub1 &&
+               echo "content" >sub1 &&
+               git add sub1 &&
+               git commit -m "Replace sub1 with file" &&
+               git checkout -b replace_file_with_sub1 &&
+               git revert HEAD &&
+
+               git checkout -b "invalid_sub1" "add_sub1" &&
+               git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 sub1 &&
+               git commit -m "Invalid sub1 commit" &&
+               git checkout -b valid_sub1 &&
+               git revert HEAD &&
+               git checkout master
+       )
+}
+
+# Helper function to replace gitfile with .git directory
+replace_gitfile_with_git_dir () {
+       (
+               cd "$1" &&
+               git_dir="$(git rev-parse --git-dir)" &&
+               rm -f .git &&
+               cp -R "$git_dir" .git &&
+               GIT_WORK_TREE=. git config --unset core.worktree
+       )
+}
+
+# Test that the .git directory in the submodule is unchanged (except for the
+# core.worktree setting, which appears only in $GIT_DIR/modules/$1/config).
+# Call this function before test_submodule_content as the latter might
+# write the index file leading to false positive index differences.
+#
+# Note that this only supports submodules at the root level of the
+# superproject, with the default name, i.e. same as its path.
+test_git_directory_is_unchanged () {
+       (
+               cd ".git/modules/$1" &&
+               # does core.worktree point at the right place?
+               test "$(git config core.worktree)" = "../../../$1" &&
+               # remove it temporarily before comparing, as
+               # "$1/.git/config" lacks it...
+               git config --unset core.worktree
+       ) &&
+       diff -r ".git/modules/$1" "$1/.git" &&
+       (
+               # ... and then restore.
+               cd ".git/modules/$1" &&
+               git config core.worktree "../../../$1"
+       )
+}
+
+# Helper function to be executed at the start of every test below, it sets up
+# the submodule repo if it doesn't exist and configures the most problematic
+# settings for diff.ignoreSubmodules.
+prolog () {
+       (test -d submodule_update_repo || create_lib_submodule_repo) &&
+       test_config_global diff.ignoreSubmodules all &&
+       test_config diff.ignoreSubmodules all
+}
+
+# Helper function to bring work tree back into the state given by the
+# commit. This includes trying to populate sub1 accordingly if it exists and
+# should be updated to an existing commit.
+reset_work_tree_to () {
+       rm -rf submodule_update &&
+       git clone submodule_update_repo submodule_update &&
+       (
+               cd submodule_update &&
+               rm -rf sub1 &&
+               git checkout -f "$1" &&
+               git status -u -s >actual &&
+               test_must_be_empty actual &&
+               sha1=$(git rev-parse --revs-only HEAD:sub1) &&
+               if test -n "$sha1" &&
+                  test $(cd "sub1" && git rev-parse --verify "$sha1^{commit}")
+               then
+                       git submodule update --init --recursive "sub1"
+               fi
+       )
+}
+
+# Test that the superproject contains the content according to commit "$1"
+# (the work tree must match the index for everything but submodules but the
+# index must exactly match the given commit including any submodule SHA-1s).
+test_superproject_content () {
+       git diff-index --cached "$1" >actual &&
+       test_must_be_empty actual &&
+       git diff-files --ignore-submodules >actual &&
+       test_must_be_empty actual
+}
+
+# Test that the given submodule at path "$1" contains the content according
+# to the submodule commit recorded in the superproject's commit "$2"
+test_submodule_content () {
+       if test $# != 2
+       then
+               echo "test_submodule_content needs two arguments"
+               return 1
+       fi &&
+       submodule="$1" &&
+       commit="$2" &&
+       test -d "$submodule"/ &&
+       if ! test -f "$submodule"/.git && ! test -d "$submodule"/.git
+       then
+               echo "Submodule $submodule is not populated"
+               return 1
+       fi &&
+       sha1=$(git rev-parse --verify "$commit:$submodule") &&
+       if test -z "$sha1"
+       then
+               echo "Couldn't retrieve SHA-1 of $submodule for $commit"
+               return 1
+       fi &&
+       (
+               cd "$submodule" &&
+               git status -u -s >actual &&
+               test_must_be_empty actual &&
+               git diff "$sha1" >actual &&
+               test_must_be_empty actual
+       )
+}
+
+# Test that the following transitions are correctly handled:
+# - Updated submodule
+# - New submodule
+# - Removed submodule
+# - Directory containing tracked files replaced by submodule
+# - Submodule replaced by tracked files in directory
+# - Submodule replaced by tracked file with the same name
+# - tracked file replaced by submodule
+#
+# The default is that submodule contents aren't changed until "git submodule
+# update" is run. And even then that command doesn't delete the work tree of
+# a removed submodule.
+#
+# Removing a submodule containing a .git directory must fail even when forced
+# to protect the history!
+#
+
+# Test that submodule contents are currently not updated when switching
+# between commits that change a submodule.
+test_submodule_switch () {
+       command="$1"
+       ######################### Appearing submodule #########################
+       # Switching to a commit letting a submodule appear creates empty dir ...
+       if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1
+       then
+               # Restoring stash fails to restore submodule index entry
+               RESULT="failure"
+       else
+               RESULT="success"
+       fi
+       test_expect_$RESULT "$command: added submodule creates empty directory" '
+               prolog &&
+               reset_work_tree_to no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_dir_is_empty sub1 &&
+                       git submodule update --init --recursive &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... and doesn't care if it already exists ...
+       test_expect_$RESULT "$command: added submodule leaves existing empty directory alone" '
+               prolog &&
+               reset_work_tree_to no_submodule &&
+               (
+                       cd submodule_update &&
+                       mkdir sub1 &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_dir_is_empty sub1 &&
+                       git submodule update --init --recursive &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... unless there is an untracked file in its place.
+       test_expect_success "$command: added submodule doesn't remove untracked unignored file with same name" '
+               prolog &&
+               reset_work_tree_to no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       >sub1 &&
+                       test_must_fail $command add_sub1 &&
+                       test_superproject_content origin/no_submodule &&
+                       test_must_be_empty sub1
+               )
+       '
+       # Replacing a tracked file with a submodule produces an empty
+       # directory ...
+       test_expect_$RESULT "$command: replace tracked file with submodule creates empty directory" '
+               prolog &&
+               reset_work_tree_to replace_sub1_with_file &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
+                       $command replace_file_with_sub1 &&
+                       test_superproject_content origin/replace_file_with_sub1 &&
+                       test_dir_is_empty sub1 &&
+                       git submodule update --init --recursive &&
+                       test_submodule_content sub1 origin/replace_file_with_sub1
+               )
+       '
+       # ... as does removing a directory with tracked files with a
+       # submodule.
+       if test "$KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR" = 1
+       then
+               # Non fast-forward merges fail with "Directory sub1 doesn't
+               # exist. sub1" because the empty submodule directory is not
+               # created
+               RESULT="failure"
+       else
+               RESULT="success"
+       fi
+       test_expect_$RESULT "$command: replace directory with submodule" '
+               prolog &&
+               reset_work_tree_to replace_sub1_with_directory &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
+                       $command replace_directory_with_sub1 &&
+                       test_superproject_content origin/replace_directory_with_sub1 &&
+                       test_dir_is_empty sub1 &&
+                       git submodule update --init --recursive &&
+                       test_submodule_content sub1 origin/replace_directory_with_sub1
+               )
+       '
+
+       ######################## Disappearing submodule #######################
+       # Removing a submodule doesn't remove its work tree ...
+       if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1
+       then
+               RESULT="failure"
+       else
+               RESULT="success"
+       fi
+       test_expect_$RESULT "$command: removed submodule leaves submodule directory and its contents in place" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t remove_sub1 origin/remove_sub1 &&
+                       $command remove_sub1 &&
+                       test_superproject_content origin/remove_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... especially when it contains a .git directory.
+       test_expect_$RESULT "$command: removed submodule leaves submodule containing a .git directory alone" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t remove_sub1 origin/remove_sub1 &&
+                       replace_gitfile_with_git_dir sub1 &&
+                       $command remove_sub1 &&
+                       test_superproject_content origin/remove_sub1 &&
+                       test_git_directory_is_unchanged sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # Replacing a submodule with files in a directory must fail as the
+       # submodule work tree isn't removed ...
+       if test "$KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES" = 1
+       then
+               # Non fast-forward merges attempt to merge the former
+               # submodule files with the newly checked out ones in the
+               # directory of the same name while it shouldn't.
+               RESULT="failure"
+       else
+               RESULT="success"
+       fi
+       test_expect_$RESULT "$command: replace submodule with a directory must fail" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+                       test_must_fail $command replace_sub1_with_directory &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... especially when it contains a .git directory.
+       test_expect_$RESULT "$command: replace submodule containing a .git directory with a directory must fail" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+                       replace_gitfile_with_git_dir sub1 &&
+                       test_must_fail $command replace_sub1_with_directory &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_git_directory_is_unchanged sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # Replacing it with a file must fail as it could throw away any local
+       # work tree changes ...
+       test_expect_failure "$command: replace submodule with a file must fail" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+                       test_must_fail $command replace_sub1_with_file &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... or even destroy unpushed parts of submodule history if that
+       # still uses a .git directory.
+       test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+                       replace_gitfile_with_git_dir sub1 &&
+                       test_must_fail $command replace_sub1_with_file &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_git_directory_is_unchanged sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+
+       ########################## Modified submodule #########################
+       # Updating a submodule sha1 doesn't update the submodule's work tree
+       if test "$KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT" = 1
+       then
+               # When cherry picking a SHA-1 update for an ignored submodule
+               # the commit incorrectly fails with "The previous cherry-pick
+               # is now empty, possibly due to conflict resolution."
+               RESULT="failure"
+       else
+               RESULT="success"
+       fi
+       test_expect_$RESULT "$command: modified submodule does not update submodule work tree" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t modify_sub1 origin/modify_sub1 &&
+                       $command modify_sub1 &&
+                       test_superproject_content origin/modify_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1 &&
+                       git submodule update &&
+                       test_submodule_content sub1 origin/modify_sub1
+               )
+       '
+
+       # Updating a submodule to an invalid sha1 doesn't update the
+       # submodule's work tree, subsequent update will fail
+       test_expect_$RESULT "$command: modified submodule does not update submodule work tree to invalid commit" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t invalid_sub1 origin/invalid_sub1 &&
+                       $command invalid_sub1 &&
+                       test_superproject_content origin/invalid_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1 &&
+                       test_must_fail git submodule update &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # Updating a submodule from an invalid sha1 doesn't update the
+       # submodule's work tree, subsequent update will succeed
+       test_expect_$RESULT "$command: modified submodule does not update submodule work tree from invalid commit" '
+               prolog &&
+               reset_work_tree_to invalid_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t valid_sub1 origin/valid_sub1 &&
+                       $command valid_sub1 &&
+                       test_superproject_content origin/valid_sub1 &&
+                       test_dir_is_empty sub1 &&
+                       git submodule update --init --recursive &&
+                       test_submodule_content sub1 origin/valid_sub1
+               )
+       '
+}
+
+# Test that submodule contents are currently not updated when switching
+# between commits that change a submodule, but throwing away local changes in
+# the superproject is allowed.
+test_submodule_forced_switch () {
+       command="$1"
+       ######################### Appearing submodule #########################
+       # Switching to a commit letting a submodule appear creates empty dir ...
+       test_expect_success "$command: added submodule creates empty directory" '
+               prolog &&
+               reset_work_tree_to no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_dir_is_empty sub1 &&
+                       git submodule update --init --recursive &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... and doesn't care if it already exists ...
+       test_expect_success "$command: added submodule leaves existing empty directory alone" '
+               prolog &&
+               reset_work_tree_to no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       mkdir sub1 &&
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_dir_is_empty sub1 &&
+                       git submodule update --init --recursive &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... unless there is an untracked file in its place.
+       test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
+               prolog &&
+               reset_work_tree_to no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       >sub1 &&
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_dir_is_empty sub1
+               )
+       '
+       # Replacing a tracked file with a submodule produces an empty
+       # directory ...
+       test_expect_success "$command: replace tracked file with submodule creates empty directory" '
+               prolog &&
+               reset_work_tree_to replace_sub1_with_file &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
+                       $command replace_file_with_sub1 &&
+                       test_superproject_content origin/replace_file_with_sub1 &&
+                       test_dir_is_empty sub1 &&
+                       git submodule update --init --recursive &&
+                       test_submodule_content sub1 origin/replace_file_with_sub1
+               )
+       '
+       # ... as does removing a directory with tracked files with a
+       # submodule.
+       test_expect_success "$command: replace directory with submodule" '
+               prolog &&
+               reset_work_tree_to replace_sub1_with_directory &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
+                       $command replace_directory_with_sub1 &&
+                       test_superproject_content origin/replace_directory_with_sub1 &&
+                       test_dir_is_empty sub1 &&
+                       git submodule update --init --recursive &&
+                       test_submodule_content sub1 origin/replace_directory_with_sub1
+               )
+       '
+
+       ######################## Disappearing submodule #######################
+       # Removing a submodule doesn't remove its work tree ...
+       test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t remove_sub1 origin/remove_sub1 &&
+                       $command remove_sub1 &&
+                       test_superproject_content origin/remove_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... especially when it contains a .git directory.
+       test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t remove_sub1 origin/remove_sub1 &&
+                       replace_gitfile_with_git_dir sub1 &&
+                       $command remove_sub1 &&
+                       test_superproject_content origin/remove_sub1 &&
+                       test_git_directory_is_unchanged sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # Replacing a submodule with files in a directory must fail as the
+       # submodule work tree isn't removed ...
+       test_expect_failure "$command: replace submodule with a directory must fail" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+                       test_must_fail $command replace_sub1_with_directory &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... especially when it contains a .git directory.
+       test_expect_failure "$command: replace submodule containing a .git directory with a directory must fail" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+                       replace_gitfile_with_git_dir sub1 &&
+                       test_must_fail $command replace_sub1_with_directory &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_git_directory_is_unchanged sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # Replacing it with a file must fail as it could throw away any local
+       # work tree changes ...
+       test_expect_failure "$command: replace submodule with a file must fail" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+                       test_must_fail $command replace_sub1_with_file &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... or even destroy unpushed parts of submodule history if that
+       # still uses a .git directory.
+       test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+                       replace_gitfile_with_git_dir sub1 &&
+                       test_must_fail $command replace_sub1_with_file &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_git_directory_is_unchanged sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+
+       ########################## Modified submodule #########################
+       # Updating a submodule sha1 doesn't update the submodule's work tree
+       test_expect_success "$command: modified submodule does not update submodule work tree" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t modify_sub1 origin/modify_sub1 &&
+                       $command modify_sub1 &&
+                       test_superproject_content origin/modify_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1 &&
+                       git submodule update &&
+                       test_submodule_content sub1 origin/modify_sub1
+               )
+       '
+       # Updating a submodule to an invalid sha1 doesn't update the
+       # submodule's work tree, subsequent update will fail
+       test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
+               prolog &&
+               reset_work_tree_to add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t invalid_sub1 origin/invalid_sub1 &&
+                       $command invalid_sub1 &&
+                       test_superproject_content origin/invalid_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1 &&
+                       test_must_fail git submodule update &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # Updating a submodule from an invalid sha1 doesn't update the
+       # submodule's work tree, subsequent update will succeed
+       test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" '
+               prolog &&
+               reset_work_tree_to invalid_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t valid_sub1 origin/valid_sub1 &&
+                       $command valid_sub1 &&
+                       test_superproject_content origin/valid_sub1 &&
+                       test_dir_is_empty sub1 &&
+                       git submodule update --init --recursive &&
+                       test_submodule_content sub1 origin/valid_sub1
+               )
+       '
+}
index 391e2b64927d7d095df2569a4e56ea075a391d78..f97c80556fac55ae1bc11d24712be21a9dac0dcf 100755 (executable)
@@ -237,4 +237,17 @@ test_expect_success 'grow / shrink' '
 
 '
 
+test_expect_success 'string interning' '
+
+test_hashmap "intern value1
+intern Value1
+intern value2
+intern value2
+" "value1
+Value1
+value2
+value2"
+
+'
+
 test_done
index 28102de1a0f987b351a423516d394180175a1f13..c164b4662a06d26bc23b58287f88023ed0a588a2 100755 (executable)
@@ -12,144 +12,144 @@ test_expect_success setup '
 
        git config core.autocrlf false &&
 
-       for w in Hello world how are you; do echo $w; done >one &&
-       for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >two &&
-       for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >three &&
+       for w in Hello world how are you; do echo $w; done >LFonly &&
+       for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >CRLFonly &&
+       for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >LFwithNUL &&
        git add . &&
 
        git commit -m initial &&
 
-       one=$(git rev-parse HEAD:one) &&
-       two=$(git rev-parse HEAD:two) &&
-       three=$(git rev-parse HEAD:three) &&
+       LFonly=$(git rev-parse HEAD:LFonly) &&
+       CRLFonly=$(git rev-parse HEAD:CRLFonly) &&
+       LFwithNUL=$(git rev-parse HEAD:LFwithNUL) &&
 
        echo happy.
 '
 
 test_expect_success 'default settings cause no changes' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git read-tree --reset -u HEAD &&
 
-       ! has_cr one &&
-       has_cr two &&
-       onediff=$(git diff one) &&
-       twodiff=$(git diff two) &&
-       threediff=$(git diff three) &&
-       test -z "$onediff" && test -z "$twodiff" && test -z "$threediff"
+       ! has_cr LFonly &&
+       has_cr CRLFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       CRLFonlydiff=$(git diff CRLFonly) &&
+       LFwithNULdiff=$(git diff LFwithNUL) &&
+       test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
 '
 
 test_expect_success 'crlf=true causes a CRLF file to be normalized' '
 
        # Backwards compatibility check
-       rm -f .gitattributes tmp one two three &&
-       echo "two crlf" > .gitattributes &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+       echo "CRLFonly crlf" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
        # Note, "normalized" means that git will normalize it if added
-       has_cr two &&
-       twodiff=$(git diff two) &&
-       test -n "$twodiff"
+       has_cr CRLFonly &&
+       CRLFonlydiff=$(git diff CRLFonly) &&
+       test -n "$CRLFonlydiff"
 '
 
 test_expect_success 'text=true causes a CRLF file to be normalized' '
 
-       rm -f .gitattributes tmp one two three &&
-       echo "two text" > .gitattributes &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+       echo "CRLFonly text" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
        # Note, "normalized" means that git will normalize it if added
-       has_cr two &&
-       twodiff=$(git diff two) &&
-       test -n "$twodiff"
+       has_cr CRLFonly &&
+       CRLFonlydiff=$(git diff CRLFonly) &&
+       test -n "$CRLFonlydiff"
 '
 
 test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=false' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf false &&
-       echo "one eol=crlf" > .gitattributes &&
+       echo "LFonly eol=crlf" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       has_cr one &&
-       onediff=$(git diff one) &&
-       test -z "$onediff"
+       has_cr LFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       test -z "$LFonlydiff"
 '
 
 test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=input' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf input &&
-       echo "one eol=crlf" > .gitattributes &&
+       echo "LFonly eol=crlf" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       has_cr one &&
-       onediff=$(git diff one) &&
-       test -z "$onediff"
+       has_cr LFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       test -z "$LFonlydiff"
 '
 
 test_expect_success 'eol=lf gives a normalized file LFs with autocrlf=true' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf true &&
-       echo "one eol=lf" > .gitattributes &&
+       echo "LFonly eol=lf" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       ! has_cr one &&
-       onediff=$(git diff one) &&
-       test -z "$onediff"
+       ! has_cr LFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       test -z "$LFonlydiff"
 '
 
 test_expect_success 'autocrlf=true does not normalize CRLF files' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf true &&
        git read-tree --reset -u HEAD &&
 
-       has_cr one &&
-       has_cr two &&
-       onediff=$(git diff one) &&
-       twodiff=$(git diff two) &&
-       threediff=$(git diff three) &&
-       test -z "$onediff" && test -z "$twodiff" && test -z "$threediff"
+       has_cr LFonly &&
+       has_cr CRLFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       CRLFonlydiff=$(git diff CRLFonly) &&
+       LFwithNULdiff=$(git diff LFwithNUL) &&
+       test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
 '
 
 test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf true &&
        echo "* text=auto" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       has_cr one &&
-       has_cr two &&
-       onediff=$(git diff one) &&
-       twodiff=$(git diff two) &&
-       threediff=$(git diff three) &&
-       test -z "$onediff" && test -n "$twodiff" && test -z "$threediff"
+       has_cr LFonly &&
+       has_cr CRLFonly &&
+       LFonlydiff=$(git diff LFonly) &&
+       CRLFonlydiff=$(git diff CRLFonly) &&
+       LFwithNULdiff=$(git diff LFwithNUL) &&
+       test -z "$LFonlydiff" -a -n "$CRLFonlydiff" -a -z "$LFwithNULdiff"
 '
 
 test_expect_success 'text=auto, autocrlf=true does not normalize binary files' '
 
-       rm -f .gitattributes tmp one two three &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
        git config core.autocrlf true &&
        echo "* text=auto" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       ! has_cr three &&
-       threediff=$(git diff three) &&
-       test -z "$threediff"
+       ! has_cr LFwithNUL &&
+       LFwithNULdiff=$(git diff LFwithNUL) &&
+       test -z "$LFwithNULdiff"
 '
 
 test_expect_success 'eol=crlf _does_ normalize binary files' '
 
-       rm -f .gitattributes tmp one two three &&
-       echo "three eol=crlf" > .gitattributes &&
+       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+       echo "LFwithNUL eol=crlf" > .gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       has_cr three &&
-       threediff=$(git diff three) &&
-       test -z "$threediff"
+       has_cr LFwithNUL &&
+       LFwithNULdiff=$(git diff LFwithNUL) &&
+       test -z "$LFwithNULdiff"
 '
 
 test_done
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
new file mode 100755 (executable)
index 0000000..72dd3e8
--- /dev/null
@@ -0,0 +1,265 @@
+#!/bin/sh
+
+test_description='CRLF conversion all combinations'
+
+. ./test-lib.sh
+
+if ! test_have_prereq EXPENSIVE
+then
+       skip_all="EXPENSIVE not set"
+       test_done
+fi
+
+
+compare_files()
+{
+       od -c <"$1" >"$1".expect &&
+       od -c <"$2" >"$2".actual &&
+       test_cmp "$1".expect "$2".actual &&
+       rm "$1".expect "$2".actual
+}
+
+compare_ws_file()
+{
+       pfx=$1
+       exp=$2.expect
+       act=$pfx.actual.$3
+       od -c <"$2" >"$exp" &&
+       od -c <"$3" >"$act" &&
+       test_cmp $exp $act &&
+       rm $exp $act
+}
+
+create_gitattributes()
+{
+       txtbin=$1
+       case "$txtbin" in
+               auto)
+               echo "*.txt text=auto" >.gitattributes
+               ;;
+               text)
+               echo "*.txt text" >.gitattributes
+               ;;
+               -text)
+               echo "*.txt -text" >.gitattributes
+               ;;
+               *)
+               echo >.gitattributes
+               ;;
+       esac
+}
+
+create_file_in_repo()
+{
+       crlf=$1
+       txtbin=$2
+       create_gitattributes "$txtbin" &&
+       for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
+       do
+               pfx=crlf_${crlf}_attr_${txtbin}_$f.txt &&
+               cp $f $pfx && git -c core.autocrlf=$crlf add $pfx
+       done &&
+       git commit -m "core.autocrlf $crlf"
+}
+
+check_files_in_repo()
+{
+       crlf=$1
+       txtbin=$2
+       lfname=$3
+       crlfname=$4
+       lfmixcrlf=$5
+       lfmixcr=$6
+       crlfnul=$7
+       pfx=crlf_${crlf}_attr_${txtbin}_ &&
+       compare_files $lfname ${pfx}LF.txt &&
+       compare_files $crlfname ${pfx}CRLF.txt &&
+       compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt &&
+       compare_files $lfmixcr ${pfx}LF_mix_CR.txt &&
+       compare_files $crlfnul ${pfx}CRLF_nul.txt
+}
+
+
+check_files_in_ws()
+{
+       eol=$1
+       crlf=$2
+       txtbin=$3
+       lfname=$4
+       crlfname=$5
+       lfmixcrlf=$6
+       lfmixcr=$7
+       crlfnul=$8
+       create_gitattributes $txtbin &&
+       git config core.autocrlf $crlf &&
+       pfx=eol_${eol}_crlf_${crlf}_attr_${txtbin}_ &&
+       src=crlf_false_attr__ &&
+       for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
+       do
+               rm $src$f.txt &&
+               if test -z "$eol"; then
+                       git checkout $src$f.txt
+               else
+                       git -c core.eol=$eol checkout $src$f.txt
+               fi
+       done
+
+
+       test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=LF" "
+               compare_ws_file $pfx $lfname    ${src}LF.txt
+       "
+       test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF" "
+               compare_ws_file $pfx $crlfname  ${src}CRLF.txt
+       "
+       test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF_mix_LF" "
+               compare_ws_file $pfx $lfmixcrlf ${src}CRLF_mix_LF.txt
+       "
+       test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=LF_mix_CR" "
+               compare_ws_file $pfx $lfmixcr   ${src}LF_mix_CR.txt
+       "
+       test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF_nul" "
+               compare_ws_file $pfx $crlfnul   ${src}CRLF_nul.txt
+       "
+}
+
+#######
+(
+       type od >/dev/null &&
+       printf "line1Q\r\nline2\r\nline3" | q_to_nul >CRLF_nul &&
+       cat >expect <<-EOF &&
+       0000000 l i n e 1 \0 \r \n l i n e 2 \r \n l
+       0000020 i n e 3
+       0000024
+EOF
+       od -c CRLF_nul | sed -e "s/[    ][       ]*/ /g" -e "s/ *$//" >actual
+       test_cmp expect actual &&
+       rm expect actual
+) || {
+               skip_all="od not found or od -c not usable"
+               exit 0
+               test_done
+}
+
+test_expect_success 'setup master' '
+       echo >.gitattributes &&
+       git checkout -b master &&
+       git add .gitattributes &&
+       git commit -m "add .gitattributes" "" &&
+       printf "line1\nline2\nline3"     >LF &&
+       printf "line1\r\nline2\r\nline3" >CRLF &&
+       printf "line1\r\nline2\nline3"   >CRLF_mix_LF &&
+       printf "line1\nline2\rline3"     >LF_mix_CR &&
+       printf "line1\r\nline2\rline3"   >CRLF_mix_CR &&
+       printf "line1Q\nline2\nline3" | q_to_nul >LF_nul
+'
+#  CRLF_nul had been created above
+
+test_expect_success 'create files' '
+       create_file_in_repo false "" &&
+       create_file_in_repo true  "" &&
+       create_file_in_repo input "" &&
+
+       create_file_in_repo false "auto" &&
+       create_file_in_repo true  "auto" &&
+       create_file_in_repo input "auto" &&
+
+       create_file_in_repo false "text" &&
+       create_file_in_repo true  "text" &&
+       create_file_in_repo input "text" &&
+
+       create_file_in_repo false "-text" &&
+       create_file_in_repo true  "-text" &&
+       create_file_in_repo input "-text" &&
+       rm -f *.txt &&
+       git reset --hard
+'
+
+test_expect_success 'commit empty gitattribues' '
+       check_files_in_repo false ""      LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+       check_files_in_repo true  ""      LF LF   LF          LF_mix_CR CRLF_nul &&
+       check_files_in_repo input ""      LF LF   LF          LF_mix_CR CRLF_nul
+'
+
+test_expect_success 'commit text=auto' '
+       check_files_in_repo false "auto"  LF LF   LF          LF_mix_CR CRLF_nul &&
+       check_files_in_repo true  "auto"  LF LF   LF          LF_mix_CR CRLF_nul &&
+       check_files_in_repo input "auto"  LF LF   LF          LF_mix_CR CRLF_nul
+'
+
+test_expect_success 'commit text' '
+       check_files_in_repo false "text"  LF LF   LF          LF_mix_CR LF_nul &&
+       check_files_in_repo true  "text"  LF LF   LF          LF_mix_CR LF_nul &&
+       check_files_in_repo input "text"  LF LF   LF          LF_mix_CR LF_nul
+'
+
+test_expect_success 'commit -text' '
+       check_files_in_repo false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+       check_files_in_repo true  "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+       check_files_in_repo input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+'
+
+################################################################################
+# Check how files in the repo are changed when they are checked out
+# How to read the table below:
+# - check_files_in_ws will check multiple files, see below
+# - parameter $1 : core.eol               lf | crlf
+# - parameter $2 : core.autocrlf          false | true | input
+# - parameter $3 : text in .gitattributs  "" (empty) | auto | text | -text
+# - parameter $4 : reference for a file with only LF in the repo
+# - parameter $5 : reference for a file with only CRLF in the repo
+# - parameter $6 : reference for a file with mixed LF and CRLF in the repo
+# - parameter $7 : reference for a file with LF and CR in the repo (does somebody uses this ?)
+# - parameter $8 : reference for a file with CRLF and a NUL (should be handled as binary when auto)
+
+check_files_in_ws lf      false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+check_files_in_ws lf      false "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
+check_files_in_ws lf      input "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+check_files_in_ws lf      false "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+check_files_in_ws lf      input "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+check_files_in_ws lf      false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws lf      input "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+###########
+#core.autocrlf=input is forbidden with core.eol=crlf
+check_files_in_ws crlf    false ""        LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws crlf    true  ""        CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+check_files_in_ws crlf    false "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
+check_files_in_ws crlf    true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
+
+check_files_in_ws crlf    false "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+check_files_in_ws crlf    true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+
+check_files_in_ws crlf    false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws crlf    true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+if test_have_prereq MINGW
+then
+check_files_in_ws ""      false ""        LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws ""      true  ""        CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws ""      false "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws ""      true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
+check_files_in_ws ""      false "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws ""      true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+check_files_in_ws ""      false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws ""      true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+
+check_files_in_ws native  false ""        LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws native  true  ""        CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws native  false "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws native  true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
+check_files_in_ws native  false "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws native  true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+check_files_in_ws native  false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+check_files_in_ws native  true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+fi
+
+test_done
diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh
new file mode 100755 (executable)
index 0000000..20526ae
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='read-tree can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+test_submodule_switch "git read-tree -u -m"
+
+test_submodule_forced_switch "git read-tree -u --reset"
+
+test_done
index 9aeb352b3dba3fd7e46a5a0732425cd2686fc9f3..4bc7141226c802ca049a76e978b6196f9e9fd7ff 100755 (executable)
@@ -48,6 +48,7 @@ invalid_ref './foo/bar'
 invalid_ref 'foo/./bar'
 invalid_ref 'foo/bar/.'
 invalid_ref '.refs/foo'
+invalid_ref 'refs/heads/foo.'
 invalid_ref 'heads/foo..bar'
 invalid_ref 'heads/foo?bar'
 valid_ref 'foo./bar'
@@ -64,7 +65,6 @@ valid_ref "$(printf 'heads/fu\303\237')"
 invalid_ref 'heads/*foo/bar' --refspec-pattern
 invalid_ref 'heads/foo*/bar' --refspec-pattern
 invalid_ref 'heads/f*o/bar' --refspec-pattern
-invalid_ref 'heads/foo*//bar' --refspec-pattern
 
 ref='foo'
 invalid_ref "$ref"
@@ -129,20 +129,6 @@ valid_ref NOT_MINGW "$ref" '--allow-onelevel --normalize'
 invalid_ref NOT_MINGW "$ref" '--refspec-pattern --normalize'
 valid_ref NOT_MINGW "$ref" '--refspec-pattern --allow-onelevel --normalize'
 
-
-valid_ref 'refs/heads/a-very-long-refname'
-invalid_ref 'refs/heads/.a-very-long-refname'
-invalid_ref 'refs/heads/abcdefgh0123..'
-invalid_ref 'refs/heads/abcdefgh01234..'
-invalid_ref 'refs/heads/abcdefgh012345..'
-invalid_ref 'refs/heads/abcdefgh0123456..'
-invalid_ref 'refs/heads/abcdefgh01234567..'
-valid_ref 'refs/heads/abcdefgh0123.a'
-valid_ref 'refs/heads/abcdefgh01234.a'
-valid_ref 'refs/heads/abcdefgh012345.a'
-valid_ref 'refs/heads/abcdefgh0123456.a'
-valid_ref 'refs/heads/abcdefgh01234567.a'
-
 test_expect_success "check-ref-format --branch @{-1}" '
        T=$(git write-tree) &&
        sha1=$(echo A | git commit-tree $T) &&
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
new file mode 100755 (executable)
index 0000000..94fb473
--- /dev/null
@@ -0,0 +1,194 @@
+#!/bin/sh
+
+test_description='split index mode tests'
+
+. ./test-lib.sh
+
+# We need total control of index splitting here
+sane_unset GIT_TEST_SPLIT_INDEX
+
+test_expect_success 'enable split index' '
+       git update-index --split-index &&
+       test-dump-split-index .git/index >actual &&
+       cat >expect <<EOF &&
+own 8299b0bcd1ac364e5f1d7768efb62fa2da79a339
+base 39d890139ee5356c7ef572216cebcd27aa41f9df
+replacements:
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'add one file' '
+       : >one &&
+       git update-index --add one &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+base 39d890139ee5356c7ef572216cebcd27aa41f9df
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+replacements:
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'disable split index' '
+       git update-index --no-split-index &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       BASE=`test-dump-split-index .git/index | grep "^own" | sed "s/own/base/"` &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+not a split index
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'enable split index again, "one" now belongs to base index"' '
+       git update-index --split-index &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+$BASE
+replacements:
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'modify original file, base index untouched' '
+       echo modified >one &&
+       git update-index one &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+replacements: 0
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'add another file, which stays index' '
+       : >two &&
+       git update-index --add two &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      two
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      two
+replacements: 0
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'remove file not in base index' '
+       git update-index --force-remove two &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+replacements: 0
+deletions:
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'remove file in base index' '
+       git update-index --force-remove one &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+$BASE
+replacements:
+deletions: 0
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'add original file back' '
+       : >one &&
+       git update-index --add one &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+EOF
+       test_cmp ls-files.expect ls-files.actual &&
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+$BASE
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+replacements:
+deletions: 0
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'add new file' '
+       : >two &&
+       git update-index --add two &&
+       git ls-files --stage >actual &&
+       cat >expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      two
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'unify index, two files remain' '
+       git update-index --no-split-index &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      two
+EOF
+       test_cmp ls-files.expect ls-files.actual
+
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<EOF &&
+not a split index
+EOF
+       test_cmp expect actual
+'
+
+test_done
index 06b18f8bc1e2991170736254bf3c558ab4673d13..6847f7582234d656563f867976a64142a3a01621 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='checkout can handle submodules'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
 
 test_expect_success 'setup' '
        mkdir submodule &&
@@ -62,4 +63,8 @@ test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/
        ! test -s actual
 '
 
+test_submodule_switch "git checkout"
+
+test_submodule_forced_switch "git checkout -f"
+
 test_done
index 29c1fb10cad93818d467780b799ff874e8fdcab0..cc830da58d920718b7b0a990a359afa9dd783b4c 100755 (executable)
@@ -7,6 +7,8 @@ test_description='skip-worktree bit test'
 
 . ./test-lib.sh
 
+sane_unset GIT_TEST_SPLIT_INDEX
+
 test_set_index_version 3
 
 cat >expect.full <<EOF
index 80e0a951ea3b699dc57530c1749a2e727ec6779d..47b5682662a6ed2e6734f9f9f101a5ddabcc22ff 100755 (executable)
@@ -169,6 +169,29 @@ test_expect_success 'default to common base in @{upstream}s reflog if no upstrea
        test_cmp expect actual
 '
 
+test_expect_success 'cherry-picked commits and fork-point work together' '
+       git checkout default-base &&
+       echo Amended >A &&
+       git commit -a --no-edit --amend &&
+       test_commit B B &&
+       test_commit new_B B "New B" &&
+       test_commit C C &&
+       git checkout default &&
+       git reset --hard default-base@{4} &&
+       test_commit D D &&
+       git cherry-pick -2 default-base^ &&
+       test_commit final_B B "Final B" &&
+       git rebase &&
+       echo Amended >expect &&
+       test_cmp A expect &&
+       echo "Final B" >expect &&
+       test_cmp B expect &&
+       echo C >expect &&
+       test_cmp C expect &&
+       echo D >expect &&
+       test_cmp D expect
+'
+
 test_expect_success 'rebase -q is quiet' '
        git checkout -b quiet topic &&
        git rebase -q master >output.out 2>&1 &&
diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh
new file mode 100755 (executable)
index 0000000..d5b896d
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='rebase can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+git_rebase () {
+       git status -su >expect &&
+       ls -1pR * >>expect &&
+       git checkout -b ours HEAD &&
+       echo x >>file1 &&
+       git add file1 &&
+       git commit -m add_x &&
+       git revert HEAD &&
+       git status -su >actual &&
+       ls -1pR * >>actual &&
+       test_cmp expect actual &&
+       git rebase "$1"
+}
+
+test_submodule_switch "git_rebase"
+
+git_rebase_interactive () {
+       git status -su >expect &&
+       ls -1pR * >>expect &&
+       git checkout -b ours HEAD &&
+       echo x >>file1 &&
+       git add file1 &&
+       git commit -m add_x &&
+       git revert HEAD &&
+       git status -su >actual &&
+       ls -1pR * >>actual &&
+       test_cmp expect actual &&
+       set_fake_editor &&
+       echo "fake-editor.sh" >.git/info/exclude &&
+       git rebase -i "$1"
+}
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+# The real reason "replace directory with submodule" fails is because a
+# directory "sub1" exists, but we reuse the suppression added for merge here
+test_submodule_switch "git_rebase_interactive"
+
+test_done
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
new file mode 100755 (executable)
index 0000000..6863b7b
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+test_description='cherry-pick can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git cherry-pick"
+
+test_done
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
new file mode 100755 (executable)
index 0000000..a1c4e02
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='revert can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+# Create a revert that moves from HEAD (including any test modifications to
+# the work tree) to $1 by first checking out $1 and reverting it. Reverting
+# the revert is the transition we test for. We tar the current work tree
+# first so we can restore the work tree test setup after doing the checkout
+# and revert.  We test here that the restored work tree content is identical
+# to that at the beginning. The last revert is then tested by the framework.
+git_revert () {
+       git status -su >expect &&
+       ls -1pR * >>expect &&
+       tar czf "$TRASH_DIRECTORY/tmp.tgz" * &&
+       git checkout "$1" &&
+       git revert HEAD &&
+       rm -rf * &&
+       tar xzf "$TRASH_DIRECTORY/tmp.tgz" &&
+       git status -su >actual &&
+       ls -1pR * >>actual &&
+       test_cmp expect actual &&
+       git revert HEAD
+}
+
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+test_submodule_switch "git_revert"
+
+test_done
diff --git a/t/t3906-stash-submodule.sh b/t/t3906-stash-submodule.sh
new file mode 100755 (executable)
index 0000000..d7219d6
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='stash apply can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+git_stash () {
+       git status -su >expect &&
+       ls -1pR * >>expect &&
+       git read-tree -u -m "$1" &&
+       git stash &&
+       git status -su >actual &&
+       ls -1pR * >>actual &&
+       test_cmp expect actual &&
+       git stash apply
+}
+
+KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES=1
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+test_submodule_switch "git_stash"
+
+test_done
index 805b055c899e7c9f5653eb740572f67417c9dcb6..6ec607211803d2685b109b7fc0d926d206bbf1d4 100755 (executable)
@@ -324,4 +324,14 @@ test_expect_success 'diff --cached -- file on unborn branch' '
        test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached_--_file0" result
 '
 
+test_expect_success 'diff-tree --stdin with log formatting' '
+       cat >expect <<-\EOF &&
+       Side
+       Third
+       Second
+       EOF
+       git rev-list master | git diff-tree --stdin --format=%s -s >actual &&
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4137-apply-submodule.sh b/t/t4137-apply-submodule.sh
new file mode 100755 (executable)
index 0000000..a9bd40a
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='git apply handling submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+apply_index () {
+       git diff --ignore-submodules=dirty "..$1" | git apply --index -
+}
+
+test_submodule_switch "apply_index"
+
+apply_3way () {
+       git diff --ignore-submodules=dirty "..$1" | git apply --3way -
+}
+
+test_submodule_switch "apply_3way"
+
+test_done
index cb03d287698f110b66e69636348caccdbe6a94f8..99ab7ca21f2673e646232d9c07f77c8ed2f5f998 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='git log'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
 
 test_expect_success setup '
 
@@ -841,4 +842,34 @@ test_expect_success 'dotdot is a parent directory' '
        test_cmp expect actual
 '
 
+test_expect_success GPG 'log --graph --show-signature' '
+       test_when_finished "git reset --hard && git checkout master" &&
+       git checkout -b signed master &&
+       echo foo >foo &&
+       git add foo &&
+       git commit -S -m signed_commit &&
+       git log --graph --show-signature -n1 signed >actual &&
+       grep "^| gpg: Signature made" actual &&
+       grep "^| gpg: Good signature" actual
+'
+
+test_expect_success GPG 'log --graph --show-signature for merged tag' '
+       test_when_finished "git reset --hard && git checkout master" &&
+       git checkout -b plain master &&
+       echo aaa >bar &&
+       git add bar &&
+       git commit -m bar_commit &&
+       git checkout -b tagged master &&
+       echo bbb >baz &&
+       git add baz &&
+       git commit -m baz_commit &&
+       git tag -s -m signed_tag_msg signed_tag &&
+       git checkout plain &&
+       git merge --no-ff -m msg signed_tag &&
+       git log --graph --show-signature -n1 plain >actual &&
+       grep "^|\\\  merged tag" actual &&
+       grep "^| | gpg: Signature made" actual &&
+       grep "^| | gpg: Good signature" actual
+'
+
 test_done
diff --git a/t/t4255-am-submodule.sh b/t/t4255-am-submodule.sh
new file mode 100755 (executable)
index 0000000..8bde7db
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+test_description='git am handling submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+am () {
+       git format-patch --stdout --ignore-submodules=dirty "..$1" | git am -
+}
+
+test_submodule_switch "am"
+
+am_3way () {
+       git format-patch --stdout --ignore-submodules=dirty "..$1" | git am --3way -
+}
+
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "am_3way"
+
+test_done
index 94553e103968433aa55fd882a3006b375999759e..b46118846ca9950684ff3880179ca467253a7970 100755 (executable)
@@ -54,6 +54,7 @@ EOF
 test_expect_success 'no shallow lines after receiving ACK ready' '
        (
                cd shallow &&
+               test_tick &&
                for i in $(test_seq 15)
                do
                        git checkout --orphan unrelated$i &&
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
new file mode 100755 (executable)
index 0000000..accfa5c
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='pull can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+reset_branch_to_HEAD () {
+       git branch -D "$1" &&
+       git checkout -b "$1" HEAD &&
+       git branch --set-upstream-to="origin/$1" "$1"
+}
+
+git_pull () {
+       reset_branch_to_HEAD "$1" &&
+       git pull
+}
+
+# pulls without conflicts
+test_submodule_switch "git_pull"
+
+git_pull_ff () {
+       reset_branch_to_HEAD "$1" &&
+       git pull --ff
+}
+
+test_submodule_switch "git_pull_ff"
+
+git_pull_ff_only () {
+       reset_branch_to_HEAD "$1" &&
+       git pull --ff-only
+}
+
+test_submodule_switch "git_pull_ff_only"
+
+git_pull_noff () {
+       reset_branch_to_HEAD "$1" &&
+       git pull --no-ff
+}
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git_pull_noff"
+
+test_done
index 432f086c063198e82ad8a2f7c8dd175a8685fdf5..3758961765635c4ce504eec1540605f65efd386a 100755 (executable)
@@ -77,12 +77,29 @@ test_expect_success "merge without conflict (--quiet)" \
        "git merge-file --quiet test.txt orig.txt new2.txt"
 
 cp new1.txt test2.txt
-test_expect_success "merge without conflict (missing LF at EOF)" \
-       "git merge-file test2.txt orig.txt new2.txt"
+test_expect_failure "merge without conflict (missing LF at EOF)" \
+       "git merge-file test2.txt orig.txt new4.txt"
 
-test_expect_success "merge result added missing LF" \
+test_expect_failure "merge result added missing LF" \
        "test_cmp test.txt test2.txt"
 
+cp new4.txt test3.txt
+test_expect_success "merge without conflict (missing LF at EOF, away from change in the other file)" \
+       "git merge-file --quiet test3.txt new2.txt new3.txt"
+
+cat > expect.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+EOF
+printf "propter nomen suum." >> expect.txt
+
+test_expect_success "merge does not add LF away of change" \
+       "test_cmp test3.txt expect.txt"
+
 cp test.txt backup.txt
 test_expect_success "merge with conflicts" \
        "test_must_fail git merge-file test.txt orig.txt new3.txt"
@@ -107,6 +124,55 @@ EOF
 test_expect_success "expected conflict markers" "test_cmp test.txt expect.txt"
 
 cp backup.txt test.txt
+
+cat > expect.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --ours" \
+       "git merge-file --ours test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
+cat > expect.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --theirs" \
+       "git merge-file --theirs test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
+cat > expect.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --union" \
+       "git merge-file --union test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
 test_expect_success "merge with conflicts, using -L" \
        "test_must_fail git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
 
@@ -260,4 +326,23 @@ test_expect_success 'marker size' '
        test_cmp expect actual
 '
 
+printf "line1\nline2\nline3" >nolf-orig.txt
+printf "line1\nline2\nline3x" >nolf-diff1.txt
+printf "line1\nline2\nline3y" >nolf-diff2.txt
+
+test_expect_success 'conflict at EOF without LF resolved by --ours' \
+       'git merge-file -p --ours nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+        printf "line1\nline2\nline3x" >expect.txt &&
+        test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict at EOF without LF resolved by --theirs' \
+       'git merge-file -p --theirs nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+        printf "line1\nline2\nline3y" >expect.txt &&
+        test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict at EOF without LF resolved by --union' \
+       'git merge-file -p --union nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+        printf "line1\nline2\nline3x\nline3y" >expect.txt &&
+        test_cmp expect.txt output.txt'
+
 test_done
diff --git a/t/t6041-bisect-submodule.sh b/t/t6041-bisect-submodule.sh
new file mode 100755 (executable)
index 0000000..c6b7aa6
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='bisect can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+git_bisect () {
+       git status -su >expect &&
+       ls -1pR * >>expect &&
+       tar czf "$TRASH_DIRECTORY/tmp.tgz" * &&
+       GOOD=$(git rev-parse --verify HEAD) &&
+       git checkout "$1" &&
+       echo "foo" >bar &&
+       git add bar &&
+       git commit -m "bisect bad" &&
+       BAD=$(git rev-parse --verify HEAD) &&
+       git reset --hard HEAD^^ &&
+       git submodule update &&
+       git bisect start &&
+       git bisect good $GOOD &&
+       rm -rf * &&
+       tar xzf "$TRASH_DIRECTORY/tmp.tgz" &&
+       git status -su >actual &&
+       ls -1pR * >>actual &&
+       test_cmp expect actual &&
+       git bisect bad $BAD
+}
+
+test_submodule_switch "git_bisect"
+
+test_done
index 68b3cb26d90c5b88d28bbd03e8d50d4527f8a19b..4d5a25eedfef50c578d776990a8a3ac44064533f 100755 (executable)
@@ -7,8 +7,9 @@ test_description='Tests replace refs functionality'
 exec </dev/null
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
 
-add_and_commit_file()
+add_and_commit_file ()
 {
     _file="$1"
     _msg="$2"
@@ -18,6 +19,38 @@ add_and_commit_file()
     git commit --quiet -m "$_file: $_msg"
 }
 
+commit_buffer_contains_parents ()
+{
+    git cat-file commit "$1" >payload &&
+    sed -n -e '/^$/q' -e '/^parent /p' <payload >actual &&
+    shift &&
+    for _parent
+    do
+       echo "parent $_parent"
+    done >expected &&
+    test_cmp expected actual
+}
+
+commit_peeling_shows_parents ()
+{
+    _parent_number=1
+    _commit="$1"
+    shift &&
+    for _parent
+    do
+       _found=$(git rev-parse --verify $_commit^$_parent_number) || return 1
+       test "$_found" = "$_parent" || return 1
+       _parent_number=$(( $_parent_number + 1 ))
+    done &&
+    test_must_fail git rev-parse --verify $_commit^$_parent_number
+}
+
+commit_has_parents ()
+{
+    commit_buffer_contains_parents "$@" &&
+    commit_peeling_shows_parents "$@"
+}
+
 HASH1=
 HASH2=
 HASH3=
@@ -27,36 +60,36 @@ HASH6=
 HASH7=
 
 test_expect_success 'set up buggy branch' '
-     echo "line 1" >> hello &&
-     echo "line 2" >> hello &&
-     echo "line 3" >> hello &&
-     echo "line 4" >> hello &&
+     echo "line 1" >>hello &&
+     echo "line 2" >>hello &&
+     echo "line 3" >>hello &&
+     echo "line 4" >>hello &&
      add_and_commit_file hello "4 lines" &&
      HASH1=$(git rev-parse --verify HEAD) &&
-     echo "line BUG" >> hello &&
-     echo "line 6" >> hello &&
-     echo "line 7" >> hello &&
-     echo "line 8" >> hello &&
+     echo "line BUG" >>hello &&
+     echo "line 6" >>hello &&
+     echo "line 7" >>hello &&
+     echo "line 8" >>hello &&
      add_and_commit_file hello "4 more lines with a BUG" &&
      HASH2=$(git rev-parse --verify HEAD) &&
-     echo "line 9" >> hello &&
-     echo "line 10" >> hello &&
+     echo "line 9" >>hello &&
+     echo "line 10" >>hello &&
      add_and_commit_file hello "2 more lines" &&
      HASH3=$(git rev-parse --verify HEAD) &&
-     echo "line 11" >> hello &&
+     echo "line 11" >>hello &&
      add_and_commit_file hello "1 more line" &&
      HASH4=$(git rev-parse --verify HEAD) &&
-     sed -e "s/BUG/5/" hello > hello.new &&
+     sed -e "s/BUG/5/" hello >hello.new &&
      mv hello.new hello &&
      add_and_commit_file hello "BUG fixed" &&
      HASH5=$(git rev-parse --verify HEAD) &&
-     echo "line 12" >> hello &&
-     echo "line 13" >> hello &&
+     echo "line 12" >>hello &&
+     echo "line 13" >>hello &&
      add_and_commit_file hello "2 more lines" &&
      HASH6=$(git rev-parse --verify HEAD) &&
-     echo "line 14" >> hello &&
-     echo "line 15" >> hello &&
-     echo "line 16" >> hello &&
+     echo "line 14" >>hello &&
+     echo "line 15" >>hello &&
+     echo "line 16" >>hello &&
      add_and_commit_file hello "again 3 more lines" &&
      HASH7=$(git rev-parse --verify HEAD)
 '
@@ -95,7 +128,7 @@ test_expect_success 'tag replaced commit' '
 '
 
 test_expect_success '"git fsck" works' '
-     git fsck master > fsck_master.out &&
+     git fsck master >fsck_master.out &&
      grep "dangling commit $R" fsck_master.out &&
      grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
      test -z "$(git fsck)"
@@ -217,14 +250,14 @@ test_expect_success 'fetch branch with replacement' '
      (
          cd clone_dir &&
          git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
-         git log --pretty=oneline parallel3 > output.txt &&
+         git log --pretty=oneline parallel3 >output.txt &&
          ! grep $PARA3 output.txt &&
-         git show $PARA3 > para3.txt &&
+         git show $PARA3 >para3.txt &&
          grep "A U Thor" para3.txt &&
          git fetch origin "refs/replace/*:refs/replace/*" &&
-         git log --pretty=oneline parallel3 > output.txt &&
+         git log --pretty=oneline parallel3 >output.txt &&
          grep $PARA3 output.txt &&
-         git show $PARA3 > para3.txt &&
+         git show $PARA3 >para3.txt &&
          grep "O Thor" para3.txt
      )
 '
@@ -302,7 +335,7 @@ test_expect_success 'test --format medium' '
                echo "$PARA3 -> $S" &&
                echo "$MYTAG -> $HASH1"
        } | sort >expected &&
-       git replace -l --format medium | sort > actual &&
+       git replace -l --format medium | sort >actual &&
        test_cmp expected actual
 '
 
@@ -314,7 +347,7 @@ test_expect_success 'test --format long' '
                echo "$PARA3 (commit) -> $S (commit)" &&
                echo "$MYTAG (tag) -> $HASH1 (commit)"
        } | sort >expected &&
-       git replace --format=long | sort > actual &&
+       git replace --format=long | sort >actual &&
        test_cmp expected actual
 '
 
@@ -351,4 +384,60 @@ test_expect_success 'replace ref cleanup' '
        test -z "$(git replace)"
 '
 
+test_expect_success '--graft with and without already replaced object' '
+       test $(git log --oneline | wc -l) = 7 &&
+       git replace --graft $HASH5 &&
+       test $(git log --oneline | wc -l) = 3 &&
+       commit_has_parents $HASH5 &&
+       test_must_fail git replace --graft $HASH5 $HASH4 $HASH3 &&
+       git replace --force -g $HASH5 $HASH4 $HASH3 &&
+       commit_has_parents $HASH5 $HASH4 $HASH3 &&
+       git replace -d $HASH5
+'
+
+test_expect_success GPG 'set up a signed commit' '
+       echo "line 17" >>hello &&
+       echo "line 18" >>hello &&
+       git add hello &&
+       test_tick &&
+       git commit --quiet -S -m "hello: 2 more lines in a signed commit" &&
+       HASH8=$(git rev-parse --verify HEAD) &&
+       git verify-commit $HASH8
+'
+
+test_expect_success GPG '--graft with a signed commit' '
+       git cat-file commit $HASH8 >orig &&
+       git replace --graft $HASH8 &&
+       git cat-file commit $HASH8 >repl &&
+       commit_has_parents $HASH8 &&
+       test_must_fail git verify-commit $HASH8 &&
+       sed -n -e "/^tree /p" -e "/^author /p" -e "/^committer /p" orig >expected &&
+       echo >>expected &&
+       sed -e "/^$/q" repl >actual &&
+       test_cmp expected actual &&
+       git replace -d $HASH8
+'
+
+test_expect_success GPG 'set up a merge commit with a mergetag' '
+       git reset --hard HEAD &&
+       git checkout -b test_branch HEAD~2 &&
+       echo "line 1 from test branch" >>hello &&
+       echo "line 2 from test branch" >>hello &&
+       git add hello &&
+       test_tick &&
+       git commit -m "hello: 2 more lines from a test branch" &&
+       HASH9=$(git rev-parse --verify HEAD) &&
+       git tag -s -m "tag for testing with a mergetag" test_tag HEAD &&
+       git checkout master &&
+       git merge -s ours test_tag &&
+       HASH10=$(git rev-parse --verify HEAD) &&
+       git cat-file commit $HASH10 | grep "^mergetag object"
+'
+
+test_expect_success GPG '--graft on a commit with a mergetag' '
+       test_must_fail git replace --graft $HASH10 $HASH8^1 &&
+       git replace --graft $HASH10 $HASH8^1 $HASH9 &&
+       git replace -d $HASH10
+'
+
 test_done
index 9496736a89eb6b0b1ece64052cd2726c516c952b..66643e4bd758aa55f4f58d70675e32f1ad173460 100755 (executable)
@@ -308,6 +308,17 @@ test_expect_success 'Prune empty commits' '
        test_cmp expect actual
 '
 
+test_expect_success 'prune empty collapsed merges' '
+       test_config merge.ff false &&
+       git rev-list HEAD >expect &&
+       test_commit to_remove_2 &&
+       git reset --hard HEAD^ &&
+       test_merge non-ff to_remove_2 &&
+       git filter-branch -f --index-filter "git update-index --remove to_remove_2.t" --prune-empty HEAD &&
+       git rev-list HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success '--remap-to-ancestor with filename filters' '
        git checkout master &&
        git reset --hard A &&
index e4ab0f5b64194da05b159782a5e41479bf47de20..0366653088417a3d694856538bb1d1bd35d36cda 100755 (executable)
@@ -1385,41 +1385,77 @@ test_expect_success 'lexical sort' '
        git tag foo1.6 &&
        git tag foo1.10 &&
        git tag -l --sort=refname "foo*" >actual &&
-       cat >expect <<EOF &&
-foo1.10
-foo1.3
-foo1.6
-EOF
+       cat >expect <<-\EOF &&
+       foo1.10
+       foo1.3
+       foo1.6
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'version sort' '
        git tag -l --sort=version:refname "foo*" >actual &&
-       cat >expect <<EOF &&
-foo1.3
-foo1.6
-foo1.10
-EOF
+       cat >expect <<-\EOF &&
+       foo1.3
+       foo1.6
+       foo1.10
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'reverse version sort' '
        git tag -l --sort=-version:refname "foo*" >actual &&
-       cat >expect <<EOF &&
-foo1.10
-foo1.6
-foo1.3
-EOF
+       cat >expect <<-\EOF &&
+       foo1.10
+       foo1.6
+       foo1.3
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'reverse lexical sort' '
        git tag -l --sort=-refname "foo*" >actual &&
-       cat >expect <<EOF &&
-foo1.6
-foo1.3
-foo1.10
-EOF
+       cat >expect <<-\EOF &&
+       foo1.6
+       foo1.3
+       foo1.10
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'configured lexical sort' '
+       git config tag.sort "v:refname" &&
+       git tag -l "foo*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.3
+       foo1.6
+       foo1.10
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'option override configured sort' '
+       git tag -l --sort=-refname "foo*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.6
+       foo1.3
+       foo1.10
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'invalid sort parameter on command line' '
+       test_must_fail git tag -l --sort=notvalid "foo*" >actual
+'
+
+test_expect_success 'invalid sort parameter in configuratoin' '
+       git config tag.sort "v:notvalid" &&
+       git tag -l "foo*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.10
+       foo1.3
+       foo1.6
+       EOF
        test_cmp expect actual
 '
 
diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh
new file mode 100755 (executable)
index 0000000..2eda6ad
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+test_description='reset can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+test_submodule_switch "git reset --keep"
+
+test_submodule_switch "git reset --merge"
+
+test_submodule_forced_switch "git reset --hard"
+
+test_done
diff --git a/t/t7613-merge-submodule.sh b/t/t7613-merge-submodule.sh
new file mode 100755 (executable)
index 0000000..d1e9fcc
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='merge can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+# merges without conflicts
+test_submodule_switch "git merge"
+
+test_submodule_switch "git merge --ff"
+
+test_submodule_switch "git merge --ff-only"
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git merge --no-ff"
+
+test_done
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
new file mode 100755 (executable)
index 0000000..93b7486
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 Ephrim Khong
+#
+
+test_description='repack involving cyclic alternate'
+. ./test-lib.sh
+
+test_expect_success setup '
+       GIT_OBJECT_DIRECTORY=.git//../.git/objects &&
+       export GIT_OBJECT_DIRECTORY &&
+       touch a &&
+       git add a &&
+       git commit -m 1 &&
+       git repack -adl &&
+       echo "$(pwd)"/.git/objects/../objects >.git/objects/info/alternates
+'
+
+test_expect_success 're-packing repository with itsself as alternate' '
+       git repack -adl &&
+       git fsck
+'
+
+test_done
index 1fc1f5f2afcf205f10d04cd1deaf5708f727332e..95f4421f71a09449297f4de75220c22c6361ac9c 100755 (executable)
@@ -177,7 +177,10 @@ test_expect_success 'detect copies' '
                level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") &&
                test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
                src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
-               test "$src" = file10 || test "$src" = file11 &&
+               case "$src" in
+               file10 | file11) : ;; # happy
+               *) false ;; # not
+               &&
                git config git-p4.detectCopies $(($level + 2)) &&
                git p4 submit &&
                p4 filelog //depot/file12 &&
@@ -191,7 +194,10 @@ test_expect_success 'detect copies' '
                level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") &&
                test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
                src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
-               test "$src" = file10 || test "$src" = file11 || test "$src" = file12 &&
+               case "$src" in
+               file10 | file11 | file12) : ;; # happy
+               *) false ;; # not
+               &&
                git config git-p4.detectCopies $(($level - 2)) &&
                git p4 submit &&
                p4 filelog //depot/file13 &&
index 0377d3e2960cc282ce1a2c7cb629245728c82092..dafd6ad21a92bac48155af39ebb9a4baaf3c4970 100644 (file)
@@ -489,6 +489,17 @@ test_path_is_dir () {
        fi
 }
 
+# Check if the directory exists and is empty as expected, barf otherwise.
+test_dir_is_empty () {
+       test_path_is_dir "$1" &&
+       if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
+       then
+               echo "Directory '$1' is not empty, it contains:"
+               ls -la "$1"
+               return 1
+       fi
+}
+
 test_path_is_missing () {
        if [ -e "$1" ]
        then
index a4795373a6a2e1f247b3cef9880f2a82309b3594..b1bc65bfb564ca85d35c42071f825360d1998392 100644 (file)
@@ -109,6 +109,10 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
 export EDITOR
 
+# Tests using GIT_TRACE typically don't want <timestamp> <file>:<line> output
+GIT_TRACE_BARE=1
+export GIT_TRACE_BARE
+
 if test -n "${TEST_GIT_INDEX_VERSION:+isset}"
 then
        GIT_INDEX_VERSION="$TEST_GIT_INDEX_VERSION"
index 9d51c92b74b5b9251470ea3b5d168a4cfbfb9939..0a6724fcc45eed89d612b0e641fd3fe6d4231fcb 100644 (file)
        Memcheck:Addr4
        fun:copy_ref
 }
-{
-       ignore-sse-check_refname_format-addr
-       Memcheck:Addr8
-       fun:check_refname_format
-}
-{
-       ignore-sse-check_refname_format-cond
-       Memcheck:Cond
-       fun:check_refname_format
-}
-{
-       ignore-sse-check_refname_format-value
-       Memcheck:Value8
-       fun:check_refname_format
-}
diff --git a/tag.c b/tag.c
index 7b07921b30641994ab3a877b6b377c5ce1f40149..82d841bf2df0990f124ca9020673352124f20561 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -40,15 +40,8 @@ struct tag *lookup_tag(const unsigned char *sha1)
 {
        struct object *obj = lookup_object(sha1);
        if (!obj)
-               return create_object(sha1, OBJ_TAG, alloc_tag_node());
-       if (!obj->type)
-               obj->type = OBJ_TAG;
-       if (obj->type != OBJ_TAG) {
-               error("Object %s is a %s, not a tag",
-                     sha1_to_hex(sha1), typename(obj->type));
-               return NULL;
-       }
-       return (struct tag *) obj;
+               return create_object(sha1, alloc_tag_node());
+       return object_as_type(obj, OBJ_TAG, 0);
 }
 
 static unsigned long parse_tag_date(const char *buf, const char *tail)
index 47eab9765f5cd172ca630b2a632a92d123354442..330ba4f4dd23e2cf21acbe8a7023eaca4ec09afe 100644 (file)
@@ -56,11 +56,12 @@ static int dump_cache_tree(struct cache_tree *it,
 
 int main(int ac, char **av)
 {
+       struct index_state istate;
        struct cache_tree *another = cache_tree();
        if (read_cache() < 0)
                die("unable to read index file");
-       cache_tree_update(another,
-                         (const struct cache_entry * const *)active_cache,
-                         active_nr, WRITE_TREE_DRY_RUN);
+       istate = the_index;
+       istate.cache_tree = another;
+       cache_tree_update(&istate, WRITE_TREE_DRY_RUN);
        return dump_cache_tree(active_cache_tree, another, "");
 }
diff --git a/test-dump-split-index.c b/test-dump-split-index.c
new file mode 100644 (file)
index 0000000..9cf3112
--- /dev/null
@@ -0,0 +1,34 @@
+#include "cache.h"
+#include "split-index.h"
+#include "ewah/ewok.h"
+
+static void show_bit(size_t pos, void *data)
+{
+       printf(" %d", (int)pos);
+}
+
+int main(int ac, char **av)
+{
+       struct split_index *si;
+       int i;
+
+       do_read_index(&the_index, av[1], 1);
+       printf("own %s\n", sha1_to_hex(the_index.sha1));
+       si = the_index.split_index;
+       if (!si) {
+               printf("not a split index\n");
+               return 0;
+       }
+       printf("base %s\n", sha1_to_hex(si->base_sha1));
+       for (i = 0; i < the_index.cache_nr; i++) {
+               struct cache_entry *ce = the_index.cache[i];
+               printf("%06o %s %d\t%s\n", ce->ce_mode,
+                      sha1_to_hex(ce->sha1), ce_stage(ce), ce->name);
+       }
+       printf("replacements:");
+       ewah_each_bit(si->replace_bitmap, show_bit, NULL);
+       printf("\ndeletions:");
+       ewah_each_bit(si->delete_bitmap, show_bit, NULL);
+       printf("\n");
+       return 0;
+}
index f5183fb9e82575c86355b690178a8c3d96175eb4..07aa7ecdeede64477b424620425826b380bf895d 100644 (file)
@@ -115,9 +115,8 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
 
                for (j = 0; j < rounds; j++) {
                        for (i = 0; i < TEST_SIZE; i++) {
-                               struct hashmap_entry key;
-                               hashmap_entry_init(&key, hashes[i]);
-                               hashmap_get(&map, &key, entries[i]->key);
+                               hashmap_get_from_hash(&map, hashes[i],
+                                                     entries[i]->key);
                        }
                }
 
@@ -199,12 +198,8 @@ int main(int argc, char *argv[])
 
                } else if (!strcmp("get", cmd) && l1) {
 
-                       /* setup static key */
-                       struct hashmap_entry key;
-                       hashmap_entry_init(&key, hash);
-
                        /* lookup entry in hashmap */
-                       entry = hashmap_get(&map, &key, p1);
+                       entry = hashmap_get_from_hash(&map, hash, p1);
 
                        /* print result */
                        if (!entry)
@@ -239,6 +234,20 @@ int main(int argc, char *argv[])
                        /* print table sizes */
                        printf("%u %u\n", map.tablesize, map.size);
 
+               } else if (!strcmp("intern", cmd) && l1) {
+
+                       /* test that strintern works */
+                       const char *i1 = strintern(p1);
+                       const char *i2 = strintern(p1);
+                       if (strcmp(i1, p1))
+                               printf("strintern(%s) returns %s\n", p1, i1);
+                       else if (i1 == p1)
+                               printf("strintern(%s) returns input pointer\n", p1);
+                       else if (i1 != i2)
+                               printf("strintern(%s) != strintern(%s)", i1, i2);
+                       else
+                               printf("%s\n", i1);
+
                } else if (!strcmp("perfhashmap", cmd) && l1 && l2) {
 
                        perf_hashmap(atoi(p1), atoi(p2));
index 4728013910cf1a36dac6a594c403c29de60b1fe6..9ebcbca9d25150e9bfca77a73ca45d2a06423f49 100644 (file)
@@ -6,12 +6,11 @@ static struct lock_file index_lock;
 
 int main(int ac, char **av)
 {
-       int fd = hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, 1);
        if (read_cache() < 0)
                die("unable to read index file");
        active_cache_tree = NULL;
-       if (write_cache(fd, active_cache, active_nr)
-           || commit_lock_file(&index_lock))
+       if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                die("unable to write index file");
        return 0;
 }
diff --git a/trace.c b/trace.c
index 08180a90bc0074f821e5f2e05988e27c174be39f..e583dc63bb8d7062f8b735e701978574e9fcbf25 100644 (file)
--- a/trace.c
+++ b/trace.c
 #include "quote.h"
 
 /* Get a trace file descriptor from "key" env variable. */
-static int get_trace_fd(const char *key, int *need_close)
+static int get_trace_fd(struct trace_key *key)
 {
-       char *trace = getenv(key);
+       static struct trace_key trace_default = { "GIT_TRACE" };
+       const char *trace;
+
+       /* use default "GIT_TRACE" if NULL */
+       if (!key)
+               key = &trace_default;
+
+       /* don't open twice */
+       if (key->initialized)
+               return key->fd;
+
+       trace = getenv(key->key);
 
        if (!trace || !strcmp(trace, "") ||
            !strcmp(trace, "0") || !strcasecmp(trace, "false"))
-               return 0;
-       if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
-               return STDERR_FILENO;
-       if (strlen(trace) == 1 && isdigit(*trace))
-               return atoi(trace);
-       if (is_absolute_path(trace)) {
+               key->fd = 0;
+       else if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
+               key->fd = STDERR_FILENO;
+       else if (strlen(trace) == 1 && isdigit(*trace))
+               key->fd = atoi(trace);
+       else if (is_absolute_path(trace)) {
                int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
                if (fd == -1) {
                        fprintf(stderr,
                                "Could not open '%s' for tracing: %s\n"
                                "Defaulting to tracing on stderr...\n",
                                trace, strerror(errno));
-                       return STDERR_FILENO;
+                       key->fd = STDERR_FILENO;
+               } else {
+                       key->fd = fd;
+                       key->need_close = 1;
                }
-               *need_close = 1;
-               return fd;
+       } else {
+               fprintf(stderr, "What does '%s' for %s mean?\n"
+                       "If you want to trace into a file, then please set "
+                       "%s to an absolute pathname (starting with /).\n"
+                       "Defaulting to tracing on stderr...\n",
+                       trace, key->key, key->key);
+               key->fd = STDERR_FILENO;
        }
 
-       fprintf(stderr, "What does '%s' for %s mean?\n", trace, key);
-       fprintf(stderr, "If you want to trace into a file, "
-               "then please set %s to an absolute pathname "
-               "(starting with /).\n", key);
-       fprintf(stderr, "Defaulting to tracing on stderr...\n");
+       key->initialized = 1;
+       return key->fd;
+}
 
-       return STDERR_FILENO;
+void trace_disable(struct trace_key *key)
+{
+       if (key->need_close)
+               close(key->fd);
+       key->fd = 0;
+       key->initialized = 1;
+       key->need_close = 0;
 }
 
 static const char err_msg[] = "Could not trace into fd given by "
        "GIT_TRACE environment variable";
 
-static void trace_vprintf(const char *key, const char *fmt, va_list ap)
+static int prepare_trace_line(const char *file, int line,
+                             struct trace_key *key, struct strbuf *buf)
 {
-       struct strbuf buf = STRBUF_INIT;
+       static struct trace_key trace_bare = TRACE_KEY_INIT(BARE);
+       struct timeval tv;
+       struct tm tm;
+       time_t secs;
 
        if (!trace_want(key))
-               return;
+               return 0;
 
        set_try_to_free_routine(NULL);  /* is never reset */
-       strbuf_vaddf(&buf, fmt, ap);
-       trace_strbuf(key, &buf);
-       strbuf_release(&buf);
+
+       /* unit tests may want to disable additional trace output */
+       if (trace_want(&trace_bare))
+               return 1;
+
+       /* print current timestamp */
+       gettimeofday(&tv, NULL);
+       secs = tv.tv_sec;
+       localtime_r(&secs, &tm);
+       strbuf_addf(buf, "%02d:%02d:%02d.%06ld ", tm.tm_hour, tm.tm_min,
+                   tm.tm_sec, (long) tv.tv_usec);
+
+#ifdef HAVE_VARIADIC_MACROS
+       /* print file:line */
+       strbuf_addf(buf, "%s:%d ", file, line);
+       /* align trace output (column 40 catches most files names in git) */
+       while (buf->len < 40)
+               strbuf_addch(buf, ' ');
+#endif
+
+       return 1;
+}
+
+static void print_trace_line(struct trace_key *key, struct strbuf *buf)
+{
+       /* append newline if missing */
+       if (buf->len && buf->buf[buf->len - 1] != '\n')
+               strbuf_addch(buf, '\n');
+
+       write_or_whine_pipe(get_trace_fd(key), buf->buf, buf->len, err_msg);
+       strbuf_release(buf);
+}
+
+static void trace_vprintf_fl(const char *file, int line, struct trace_key *key,
+                            const char *format, va_list ap)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       if (!prepare_trace_line(file, line, key, &buf))
+               return;
+
+       strbuf_vaddf(&buf, format, ap);
+       print_trace_line(key, &buf);
+}
+
+static void trace_argv_vprintf_fl(const char *file, int line,
+                                 const char **argv, const char *format,
+                                 va_list ap)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       if (!prepare_trace_line(file, line, NULL, &buf))
+               return;
+
+       strbuf_vaddf(&buf, format, ap);
+
+       sq_quote_argv(&buf, argv, 0);
+       print_trace_line(NULL, &buf);
+}
+
+void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
+                    const struct strbuf *data)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       if (!prepare_trace_line(file, line, key, &buf))
+               return;
+
+       strbuf_addbuf(&buf, data);
+       print_trace_line(key, &buf);
+}
+
+static struct trace_key trace_perf_key = TRACE_KEY_INIT(PERFORMANCE);
+
+static void trace_performance_vprintf_fl(const char *file, int line,
+                                        uint64_t nanos, const char *format,
+                                        va_list ap)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       if (!prepare_trace_line(file, line, &trace_perf_key, &buf))
+               return;
+
+       strbuf_addf(&buf, "performance: %.9f s", (double) nanos / 1000000000);
+
+       if (format && *format) {
+               strbuf_addstr(&buf, ": ");
+               strbuf_vaddf(&buf, format, ap);
+       }
+
+       print_trace_line(&trace_perf_key, &buf);
 }
 
-__attribute__((format (printf, 2, 3)))
-void trace_printf_key(const char *key, const char *fmt, ...)
+#ifndef HAVE_VARIADIC_MACROS
+
+void trace_printf(const char *format, ...)
 {
        va_list ap;
-       va_start(ap, fmt);
-       trace_vprintf(key, fmt, ap);
+       va_start(ap, format);
+       trace_vprintf_fl(NULL, 0, NULL, format, ap);
        va_end(ap);
 }
 
-void trace_printf(const char *fmt, ...)
+void trace_printf_key(struct trace_key *key, const char *format, ...)
 {
        va_list ap;
-       va_start(ap, fmt);
-       trace_vprintf("GIT_TRACE", fmt, ap);
+       va_start(ap, format);
+       trace_vprintf_fl(NULL, 0, key, format, ap);
        va_end(ap);
 }
 
-void trace_strbuf(const char *key, const struct strbuf *buf)
+void trace_argv_printf(const char **argv, const char *format, ...)
 {
-       int fd, need_close = 0;
-
-       fd = get_trace_fd(key, &need_close);
-       if (!fd)
-               return;
+       va_list ap;
+       va_start(ap, format);
+       trace_argv_vprintf_fl(NULL, 0, argv, format, ap);
+       va_end(ap);
+}
 
-       write_or_whine_pipe(fd, buf->buf, buf->len, err_msg);
+void trace_strbuf(const char *key, const struct strbuf *data)
+{
+       trace_strbuf_fl(NULL, 0, key, data);
+}
 
-       if (need_close)
-               close(fd);
+void trace_performance(uint64_t nanos, const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       trace_performance_vprintf_fl(NULL, 0, nanos, format, ap);
+       va_end(ap);
 }
 
-void trace_argv_printf(const char **argv, const char *fmt, ...)
+void trace_performance_since(uint64_t start, const char *format, ...)
 {
-       struct strbuf buf = STRBUF_INIT;
        va_list ap;
-       int fd, need_close = 0;
+       va_start(ap, format);
+       trace_performance_vprintf_fl(NULL, 0, getnanotime() - start,
+                                    format, ap);
+       va_end(ap);
+}
 
-       fd = get_trace_fd("GIT_TRACE", &need_close);
-       if (!fd)
-               return;
+#else
 
-       set_try_to_free_routine(NULL);  /* is never reset */
-       va_start(ap, fmt);
-       strbuf_vaddf(&buf, fmt, ap);
+void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
+                        const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       trace_vprintf_fl(file, line, key, format, ap);
        va_end(ap);
+}
 
-       sq_quote_argv(&buf, argv, 0);
-       strbuf_addch(&buf, '\n');
-       write_or_whine_pipe(fd, buf.buf, buf.len, err_msg);
-       strbuf_release(&buf);
+void trace_argv_printf_fl(const char *file, int line, const char **argv,
+                         const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       trace_argv_vprintf_fl(file, line, argv, format, ap);
+       va_end(ap);
+}
 
-       if (need_close)
-               close(fd);
+void trace_performance_fl(const char *file, int line, uint64_t nanos,
+                             const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       trace_performance_vprintf_fl(file, line, nanos, format, ap);
+       va_end(ap);
 }
 
+#endif /* HAVE_VARIADIC_MACROS */
+
+
 static const char *quote_crnl(const char *path)
 {
        static char new_path[PATH_MAX];
@@ -156,11 +296,11 @@ static const char *quote_crnl(const char *path)
 /* FIXME: move prefix to startup_info struct and get rid of this arg */
 void trace_repo_setup(const char *prefix)
 {
-       static const char *key = "GIT_TRACE_SETUP";
+       static struct trace_key key = TRACE_KEY_INIT(SETUP);
        const char *git_work_tree;
        char cwd[PATH_MAX];
 
-       if (!trace_want(key))
+       if (!trace_want(&key))
                return;
 
        if (!getcwd(cwd, PATH_MAX))
@@ -172,18 +312,117 @@ void trace_repo_setup(const char *prefix)
        if (!prefix)
                prefix = "(null)";
 
-       trace_printf_key(key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
-       trace_printf_key(key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
-       trace_printf_key(key, "setup: cwd: %s\n", quote_crnl(cwd));
-       trace_printf_key(key, "setup: prefix: %s\n", quote_crnl(prefix));
+       trace_printf_key(&key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
+       trace_printf_key(&key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
+       trace_printf_key(&key, "setup: cwd: %s\n", quote_crnl(cwd));
+       trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));
 }
 
-int trace_want(const char *key)
+int trace_want(struct trace_key *key)
 {
-       const char *trace = getenv(key);
+       return !!get_trace_fd(key);
+}
 
-       if (!trace || !strcmp(trace, "") ||
-           !strcmp(trace, "0") || !strcasecmp(trace, "false"))
+#ifdef HAVE_CLOCK_GETTIME
+
+static inline uint64_t highres_nanos(void)
+{
+       struct timespec ts;
+       if (clock_gettime(CLOCK_MONOTONIC, &ts))
                return 0;
-       return 1;
+       return (uint64_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
+}
+
+#elif defined (GIT_WINDOWS_NATIVE)
+
+static inline uint64_t highres_nanos(void)
+{
+       static uint64_t high_ns, scaled_low_ns;
+       static int scale;
+       LARGE_INTEGER cnt;
+
+       if (!scale) {
+               if (!QueryPerformanceFrequency(&cnt))
+                       return 0;
+
+               /* high_ns = number of ns per cnt.HighPart */
+               high_ns = (1000000000LL << 32) / (uint64_t) cnt.QuadPart;
+
+               /*
+                * Number of ns per cnt.LowPart is 10^9 / frequency (or
+                * high_ns >> 32). For maximum precision, we scale this factor
+                * so that it just fits within 32 bit (i.e. won't overflow if
+                * multiplied with cnt.LowPart).
+                */
+               scaled_low_ns = high_ns;
+               scale = 32;
+               while (scaled_low_ns >= 0x100000000LL) {
+                       scaled_low_ns >>= 1;
+                       scale--;
+               }
+       }
+
+       /* if QPF worked on initialization, we expect QPC to work as well */
+       QueryPerformanceCounter(&cnt);
+
+       return (high_ns * cnt.HighPart) +
+              ((scaled_low_ns * cnt.LowPart) >> scale);
+}
+
+#else
+# define highres_nanos() 0
+#endif
+
+static inline uint64_t gettimeofday_nanos(void)
+{
+       struct timeval tv;
+       gettimeofday(&tv, NULL);
+       return (uint64_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
+}
+
+/*
+ * Returns nanoseconds since the epoch (01/01/1970), for performance tracing
+ * (i.e. favoring high precision over wall clock time accuracy).
+ */
+inline uint64_t getnanotime(void)
+{
+       static uint64_t offset;
+       if (offset > 1) {
+               /* initialization succeeded, return offset + high res time */
+               return offset + highres_nanos();
+       } else if (offset == 1) {
+               /* initialization failed, fall back to gettimeofday */
+               return gettimeofday_nanos();
+       } else {
+               /* initialize offset if high resolution timer works */
+               uint64_t now = gettimeofday_nanos();
+               uint64_t highres = highres_nanos();
+               if (highres)
+                       offset = now - highres;
+               else
+                       offset = 1;
+               return now;
+       }
+}
+
+static uint64_t command_start_time;
+static struct strbuf command_line = STRBUF_INIT;
+
+static void print_command_performance_atexit(void)
+{
+       trace_performance_since(command_start_time, "git command:%s",
+                               command_line.buf);
+}
+
+void trace_command_performance(const char **argv)
+{
+       if (!trace_want(&trace_perf_key))
+               return;
+
+       if (!command_start_time)
+               atexit(print_command_performance_atexit);
+
+       strbuf_reset(&command_line);
+       sq_quote_argv(&command_line, argv, 0);
+       command_start_time = getnanotime();
 }
diff --git a/trace.h b/trace.h
new file mode 100644 (file)
index 0000000..ae6a332
--- /dev/null
+++ b/trace.h
@@ -0,0 +1,113 @@
+#ifndef TRACE_H
+#define TRACE_H
+
+#include "git-compat-util.h"
+#include "strbuf.h"
+
+struct trace_key {
+       const char * const key;
+       int fd;
+       unsigned int initialized : 1;
+       unsigned int  need_close : 1;
+};
+
+#define TRACE_KEY_INIT(name) { "GIT_TRACE_" #name, 0, 0, 0 }
+
+extern void trace_repo_setup(const char *prefix);
+extern int trace_want(struct trace_key *key);
+extern void trace_disable(struct trace_key *key);
+extern uint64_t getnanotime(void);
+extern void trace_command_performance(const char **argv);
+
+#ifndef HAVE_VARIADIC_MACROS
+
+__attribute__((format (printf, 1, 2)))
+extern void trace_printf(const char *format, ...);
+
+__attribute__((format (printf, 2, 3)))
+extern void trace_printf_key(struct trace_key *key, const char *format, ...);
+
+__attribute__((format (printf, 2, 3)))
+extern void trace_argv_printf(const char **argv, const char *format, ...);
+
+extern void trace_strbuf(struct trace_key *key, const struct strbuf *data);
+
+/* Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled. */
+__attribute__((format (printf, 2, 3)))
+extern void trace_performance(uint64_t nanos, const char *format, ...);
+
+/* Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled. */
+__attribute__((format (printf, 2, 3)))
+extern void trace_performance_since(uint64_t start, const char *format, ...);
+
+#else
+
+/*
+ * Macros to add file:line - see above for C-style declarations of how these
+ * should be used.
+ */
+
+/*
+ * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The
+ * default is __FILE__, as it is consistent with assert(), and static function
+ * names are not necessarily unique.
+ *
+ * __FILE__ ":" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied
+ * by the preprocessor as a string literal, and __FUNCTION__ is filled in by
+ * the compiler as a string constant.
+ */
+#ifndef TRACE_CONTEXT
+# define TRACE_CONTEXT __FILE__
+#endif
+
+/*
+ * Note: with C99 variadic macros, __VA_ARGS__ must include the last fixed
+ * parameter ('format' in this case). Otherwise, a call without variable
+ * arguments will have a surplus ','. E.g.:
+ *
+ *  #define foo(format, ...) bar(format, __VA_ARGS__)
+ *  foo("test");
+ *
+ * will expand to
+ *
+ *  bar("test",);
+ *
+ * which is invalid (note the ',)'). With GNUC, '##__VA_ARGS__' drops the
+ * comma, but this is non-standard.
+ */
+
+#define trace_printf(...) \
+       trace_printf_key_fl(TRACE_CONTEXT, __LINE__, NULL, __VA_ARGS__)
+
+#define trace_printf_key(key, ...) \
+       trace_printf_key_fl(TRACE_CONTEXT, __LINE__, key, __VA_ARGS__)
+
+#define trace_argv_printf(argv, ...) \
+       trace_argv_printf_fl(TRACE_CONTEXT, __LINE__, argv, __VA_ARGS__)
+
+#define trace_strbuf(key, data) \
+       trace_strbuf_fl(TRACE_CONTEXT, __LINE__, key, data)
+
+#define trace_performance(nanos, ...) \
+       trace_performance_fl(TRACE_CONTEXT, __LINE__, nanos, __VA_ARGS__)
+
+#define trace_performance_since(start, ...) \
+       trace_performance_fl(TRACE_CONTEXT, __LINE__, getnanotime() - (start), \
+                            __VA_ARGS__)
+
+/* backend functions, use non-*fl macros instead */
+__attribute__((format (printf, 4, 5)))
+extern void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
+                               const char *format, ...);
+__attribute__((format (printf, 4, 5)))
+extern void trace_argv_printf_fl(const char *file, int line, const char **argv,
+                                const char *format, ...);
+extern void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
+                           const struct strbuf *data);
+__attribute__((format (printf, 4, 5)))
+extern void trace_performance_fl(const char *file, int line,
+                                uint64_t nanos, const char *fmt, ...);
+
+#endif /* HAVE_VARIADIC_MACROS */
+
+#endif /* TRACE_H */
index 59c9727d8d63d548001513c1560b5c7a7e065b95..662421bb5e076177f0fc320330287d3da50303a5 100644 (file)
@@ -263,32 +263,20 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 static int fetch_objs_via_rsync(struct transport *transport,
                                int nr_objs, struct ref **to_fetch)
 {
-       struct strbuf buf = STRBUF_INIT;
        struct child_process rsync;
-       const char *args[8];
-       int result;
-
-       strbuf_addstr(&buf, rsync_url(transport->url));
-       strbuf_addstr(&buf, "/objects/");
 
        memset(&rsync, 0, sizeof(rsync));
-       rsync.argv = args;
        rsync.stdout_to_stderr = 1;
-       args[0] = "rsync";
-       args[1] = (transport->verbose > 1) ? "-rv" : "-r";
-       args[2] = "--ignore-existing";
-       args[3] = "--exclude";
-       args[4] = "info";
-       args[5] = buf.buf;
-       args[6] = get_object_directory();
-       args[7] = NULL;
+       argv_array_push(&rsync.args, "rsync");
+       argv_array_push(&rsync.args, (transport->verbose > 1) ? "-rv" : "-r");
+       argv_array_push(&rsync.args, "--ignore-existing");
+       argv_array_push(&rsync.args, "--exclude");
+       argv_array_push(&rsync.args, "info");
+       argv_array_pushf(&rsync.args, "%s/objects/", rsync_url(transport->url));
+       argv_array_push(&rsync.args, get_object_directory());
 
        /* NEEDSWORK: handle one level of alternates */
-       result = run_command(&rsync);
-
-       strbuf_release(&buf);
-
-       return result;
+       return run_command(&rsync);
 }
 
 static int write_one_ref(const char *name, const unsigned char *sha1,
@@ -1188,10 +1176,8 @@ int transport_push(struct transport *transport,
                if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
                              TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
-                       struct string_list needs_pushing;
+                       struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 
-                       memset(&needs_pushing, 0, sizeof(struct string_list));
-                       needs_pushing.strdup_strings = 1;
                        for (; ref; ref = ref->next)
                                if (!is_null_sha1(ref->new_sha1) &&
                                    find_unpushed_submodules(ref->new_sha1,
@@ -1371,11 +1357,11 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,
        while (other[len-1] == '/')
                other[--len] = '\0';
        if (len < 8 || memcmp(other + len - 8, "/objects", 8))
-               return 0;
+               goto out;
        /* Is this a git repository with refs? */
        memcpy(other + len - 8, "/refs", 6);
        if (!is_directory(other))
-               return 0;
+               goto out;
        other[len - 8] = '\0';
        remote = remote_get(other);
        transport = transport_get(remote, other);
@@ -1384,6 +1370,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,
             extra = extra->next)
                cb->fn(extra, cb->data);
        transport_disconnect(transport);
+out:
        free(other);
        return 0;
 }
diff --git a/tree.c b/tree.c
index c8c49d7b78174199da94d802e1ca7037866b5f04..bb02c1caa4ff6a8af40b89498de3af3381272999 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -183,15 +183,8 @@ struct tree *lookup_tree(const unsigned char *sha1)
 {
        struct object *obj = lookup_object(sha1);
        if (!obj)
-               return create_object(sha1, OBJ_TREE, alloc_tree_node());
-       if (!obj->type)
-               obj->type = OBJ_TREE;
-       if (obj->type != OBJ_TREE) {
-               error("Object %s is a %s, not a tree",
-                     sha1_to_hex(sha1), typename(obj->type));
-               return NULL;
-       }
-       return (struct tree *) obj;
+               return create_object(sha1, alloc_tree_node());
+       return object_as_type(obj, OBJ_TREE, 0);
 }
 
 int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
index 01f119f9700791ddc59a81cdc80fd365e678622a..91bd6b89d4946bbd80a0b9e9e7d6af21d6a2f61a 100644 (file)
@@ -99,11 +99,12 @@ int unix_stream_listen(const char *path)
        struct sockaddr_un sa;
        struct unix_sockaddr_context ctx;
 
+       unlink(path);
+
        if (unix_sockaddr_init(&sa, path, &ctx) < 0)
                return -1;
        fd = unix_stream_socket();
 
-       unlink(path);
        if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
                goto fail;
 
index 0ac39e93a08733ee46f42dfa1170af7333ae43e5..c6aa8fb993aa4bd92e4269d7a85c6b45fe9801e8 100644 (file)
@@ -8,6 +8,7 @@
 #include "progress.h"
 #include "refs.h"
 #include "attr.h"
+#include "split-index.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -241,7 +242,9 @@ static int verify_absent_sparse(const struct cache_entry *ce,
                                enum unpack_trees_error_types,
                                struct unpack_trees_options *o);
 
-static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
+static int apply_sparse_checkout(struct index_state *istate,
+                                struct cache_entry *ce,
+                                struct unpack_trees_options *o)
 {
        int was_skip_worktree = ce_skip_worktree(ce);
 
@@ -249,6 +252,10 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
                ce->ce_flags |= CE_SKIP_WORKTREE;
        else
                ce->ce_flags &= ~CE_SKIP_WORKTREE;
+       if (was_skip_worktree != ce_skip_worktree(ce)) {
+               ce->ce_flags |= CE_UPDATE_IN_BASE;
+               istate->cache_changed |= CE_ENTRY_CHANGED;
+       }
 
        /*
         * if (!was_skip_worktree && !ce_skip_worktree()) {
@@ -1009,6 +1016,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        state.force = 1;
        state.quiet = 1;
        state.refresh_cache = 1;
+       state.istate = &o->result;
 
        memset(&el, 0, sizeof(el));
        if (!core_apply_sparse_checkout || !o->update)
@@ -1025,6 +1033,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        o->result.timestamp.sec = o->src_index->timestamp.sec;
        o->result.timestamp.nsec = o->src_index->timestamp.nsec;
        o->result.version = o->src_index->version;
+       o->result.split_index = o->src_index->split_index;
+       if (o->result.split_index)
+               o->result.split_index->refcount++;
+       hashcpy(o->result.sha1, o->src_index->sha1);
        o->merge_size = len;
        mark_all_ce_unused(o->src_index);
 
@@ -1115,7 +1127,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                                ret = -1;
                        }
 
-                       if (apply_sparse_checkout(ce, o)) {
+                       if (apply_sparse_checkout(&o->result, ce, o)) {
                                if (!o->show_all_errors)
                                        goto return_failed;
                                ret = -1;
@@ -1243,7 +1255,7 @@ static void invalidate_ce_path(const struct cache_entry *ce,
                               struct unpack_trees_options *o)
 {
        if (ce)
-               cache_tree_invalidate_path(o->src_index->cache_tree, ce->name);
+               cache_tree_invalidate_path(o->src_index, ce->name);
 }
 
 /*
diff --git a/url.c b/url.c
index 335d97d3f74e5b7d7139e223fbe1d19837a72e81..7ca2a69e1091fc57cb292052015c920499fe3379 100644 (file)
--- a/url.c
+++ b/url.c
@@ -121,7 +121,7 @@ void end_url_with_slash(struct strbuf *buf, const char *url)
 {
        strbuf_addstr(buf, url);
        if (buf->len && buf->buf[buf->len - 1] != '/')
-               strbuf_addstr(buf, "/");
+               strbuf_addch(buf, '/');
 }
 
 void str_end_url_with_slash(const char *url, char **dest) {
index 882cfe9fb050efe84e2330b9aea76685c6a7f891..27da5296be253844e0f2bc8d5996faf343192828 100644 (file)
@@ -574,14 +574,11 @@ static void wt_status_collect_untracked(struct wt_status *s)
 {
        int i;
        struct dir_struct dir;
-       struct timeval t_begin;
+       uint64_t t_begin = getnanotime();
 
        if (!s->show_untracked_files)
                return;
 
-       if (advice_status_u_option)
-               gettimeofday(&t_begin, NULL);
-
        memset(&dir, 0, sizeof(dir));
        if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
                dir.flags |=
@@ -612,13 +609,8 @@ static void wt_status_collect_untracked(struct wt_status *s)
        free(dir.ignored);
        clear_directory(&dir);
 
-       if (advice_status_u_option) {
-               struct timeval t_end;
-               gettimeofday(&t_end, NULL);
-               s->untracked_in_ms =
-                       (uint64_t)t_end.tv_sec * 1000 + t_end.tv_usec / 1000 -
-                       ((uint64_t)t_begin.tv_sec * 1000 + t_begin.tv_usec / 1000);
-       }
+       if (advice_status_u_option)
+               s->untracked_in_ms = (getnanotime() - t_begin) / 1000000;
 }
 
 void wt_status_collect(struct wt_status *s)
index 9e13b25abc90350de4488276074dc282edc216b1..625198e0585c52c20c778790c82126efd1b57009 100644 (file)
@@ -245,11 +245,11 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
                                              dest ? dest + size : NULL);
                        /* Postimage from side #1 */
                        if (m->mode & 1)
-                               size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+                               size += xdl_recs_copy(xe1, m->i1, m->chg1, (m->mode & 2),
                                                      dest ? dest + size : NULL);
                        /* Postimage from side #2 */
                        if (m->mode & 2)
-                               size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+                               size += xdl_recs_copy(xe2, m->i2, m->chg2, 0,
                                                      dest ? dest + size : NULL);
                } else
                        continue;