Merge branch 'jk/skip-prefix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 16 Jul 2014 18:33:06 +0000 (11:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 16 Jul 2014 18:33:06 +0000 (11:33 -0700)
One more to an already graduated topic.

* jk/skip-prefix:
tag: use skip_prefix instead of magic numbers

183 files changed:
.gitignore
Documentation/CodingGuidelines
Documentation/RelNotes/2.0.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.0.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.1.0.txt
Documentation/config.txt
Documentation/fetch-options.txt
Documentation/git-fetch.txt
Documentation/git-replace.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-submodule.txt
Documentation/git-update-index.txt
Documentation/git-verify-commit.txt [new file with mode: 0644]
Documentation/git.txt
Documentation/gitrepository-layout.txt
Documentation/pull-fetch-param.txt
Documentation/technical/api-strbuf.txt
Documentation/technical/http-protocol.txt
Documentation/technical/index-format.txt
Makefile
alloc.c
builtin.h
builtin/add.c
builtin/apply.c
builtin/blame.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit-tree.c
builtin/commit.c
builtin/diff-tree.c
builtin/fast-export.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/index-pack.c
builtin/log.c
builtin/merge.c
builtin/mv.c
builtin/name-rev.c
builtin/pack-objects.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/remote.c
builtin/repack.c
builtin/replace.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/rm.c
builtin/show-branch.c
builtin/tag.c
builtin/update-index.c
builtin/verify-commit.c [new file with mode: 0644]
builtin/verify-pack.c
cache-tree.c
cache-tree.h
cache.h
check_bindir
command-list.txt
commit-slab.h
commit.c
commit.h
compat/mingw.c
compat/mingw.h
compat/win32/dirent.c
compat/win32/dirent.h
compat/winansi.c
config.mak.uname
connected.c
contrib/completion/git-completion.bash
contrib/examples/git-clone.sh
contrib/examples/git-commit.sh
contrib/examples/git-merge.sh
contrib/examples/git-repack.sh
contrib/examples/git-resolve.sh
daemon.c
dir.c
entry.c
environment.c
ewah/ewah_io.c
ewah/ewok.h
fetch-pack.c
fsck.c
git-bisect.sh
git-compat-util.h
git-filter-branch.sh
git-gui/git-gui.sh
git-gui/lib/diff.tcl
git-mergetool.sh
git-p4.py
git-rebase--interactive.sh
git-rebase--merge.sh
git-send-email.perl
git-submodule.sh
git.c
gitk-git/gitk
gitk-git/po/vi.po [new file with mode: 0644]
gpg-interface.c
gpg-interface.h
help.c
http-push.c
http-walker.c
http.c
line-log.c
lockfile.c
log-tree.c
match-trees.c
merge-recursive.c
merge.c
name-hash.c
notes-cache.c
notes-merge.c
notes-utils.c
notes-utils.h
object.c
preload-index.c
pretty.c
read-cache.c
refs.c
remote.c
rerere.c
resolve-undo.c
revision.c
sequencer.c
sha1_file.c
sha1_name.c
shell.c
split-index.c [new file with mode: 0644]
split-index.h [new file with mode: 0644]
strbuf.c
strbuf.h
submodule.c
symlinks.c
t/Makefile
t/lib-httpd.sh
t/lib-httpd/error.sh
t/perf/p5310-pack-bitmaps.sh
t/t0001-init.sh
t/t0008-ignores.sh
t/t0021-conversion.sh
t/t0025-crlf-auto.sh
t/t0026-eol-config.sh
t/t0027-auto-crlf.sh [new file with mode: 0755]
t/t1402-check-ref-format.sh
t/t1700-split-index.sh [new file with mode: 0755]
t/t2104-update-index-skip-worktree.sh
t/t3302-notes-index-expensive.sh
t/t3402-rebase-merge.sh
t/t3419-rebase-patch-id.sh
t/t4102-apply-rename.sh
t/t4202-log.sh
t/t5000-tar-tree.sh
t/t5003-archive-zip.sh
t/t5310-pack-bitmaps.sh
t/t5403-post-checkout-hook.sh
t/t5510-fetch.sh
t/t5550-http-fetch-dumb.sh
t/t5551-http-fetch-smart.sh
t/t6006-rev-list-format.sh
t/t6023-merge-file.sh
t/t7003-filter-branch.sh
t/t7300-clean.sh
t/t7510-signed-commit.sh
t/t7700-repack.sh
t/t9001-send-email.sh
t/t9814-git-p4-rename.sh
t/t9902-completion.sh
t/test-lib-functions.sh
t/test-lib.sh
t/valgrind/default.supp
test-dump-cache-tree.c
test-dump-split-index.c [new file with mode: 0644]
test-scrap-cache-tree.c
tree-walk.c
unicode_width.h
unpack-trees.c
walker.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 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
diff --git a/Documentation/RelNotes/2.0.1.txt b/Documentation/RelNotes/2.0.1.txt
new file mode 100644 (file)
index 0000000..ce5579d
--- /dev/null
@@ -0,0 +1,115 @@
+Git v2.0.1 Release Notes
+========================
+
+ * We used to unconditionally disable the pager in the pager process
+   we spawn to feed out output, but that prevented people who want to
+   run "less" within "less" from doing so.
+
+ * Tools that read diagnostic output in our standard error stream do
+   not want to see terminal control sequence (e.g. erase-to-eol).
+   Detect them by checking if the standard error stream is connected
+   to a tty.
+ * Reworded the error message given upon a failure to open an existing
+   loose object file due to e.g. permission issues; it was reported as
+   the object being corrupt, but that is not quite true.
+
+ * "git log -2master" is a common typo that shows two commits starting
+   from whichever random branch that is not 'master' that happens to
+   be checked out currently.
+
+ * 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
+   in the example) with padding to column-align the output, but did
+   not take into account that number of bytes and number of display
+   columns are different.
+
+ * The "mailmap.file" configuration option did not support the tilde
+   expansion (i.e. ~user/path and ~/path).
+
+ * The completion scripts (in contrib/) did not know about quite a few
+   options that are common between "git merge" and "git pull", and a
+   couple of options unique to "git merge".
+
+ * "--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.
+
+ * "git blame" miscounted number of columns needed to show localized
+   timestamps, resulting in jaggy left-side-edge of the source code
+   lines in its output.
+
+ * "git blame" assigned the blame to the copy in the working-tree if
+   the repository is set to core.autocrlf=input and the file used CRLF
+   line endings.
+
+ * "git commit --allow-empty-message -C $commit" did not work when the
+   commit did not have any log message.
+
+ * "git diff --find-copies-harder" sometimes pretended as if the mode
+   bits have changed for paths that are marked with assume-unchanged
+   bit.
+
+ * "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.
+
+ * "git gc --auto" was recently changed to run in the background to
+   give control back early to the end-user sitting in front of the
+   terminal, but it forgot that housekeeping involving reflogs should
+   be done without other processes competing for accesses to the refs.
+
+ * "git grep -O" to show the lines that hit in the pager did not work
+   well with case insensitive search.  We now spawn "less" with its
+   "-I" option when it is used as the pager (which is the default).
+
+ * We used to disable threaded "git index-pack" on platforms without
+   thread-safe pread(); use a different workaround for such
+   platforms to allow threaded "git index-pack".
+
+ * The error reporting from "git index-pack" has been improved to
+   distinguish missing objects from type errors.
+
+ * "git mailinfo" used to read beyond the end of header string while
+   parsing an incoming e-mail message to extract the patch.
+
+ * On a case insensitive filesystem, merge-recursive incorrectly
+   deleted the file that is to be renamed to a name that is the same
+   except for case differences.
+
+ * "git pack-objects" unnecessarily copied the previous contents when
+   extending the hashtable, even though it will populate the table
+   from scratch anyway.
+
+ * "git rerere forget" did not work well when merge.conflictstyle
+   was set to a non-default value.
+
+ * "git remote rm" and "git remote prune" can involve removing many
+   refs at once, which is not a very efficient thing to do when very
+   many refs exist in the packed-refs file.
+
+ * "git log --exclude=<glob> --all | git shortlog" worked as expected,
+   but "git shortlog --exclude=<glob> --all", which is supposed to be
+   identical to the above pipeline, was not accepted at the command
+   line argument parser level.
+
+ * The autostash mode of "git rebase -i" did not restore the dirty
+   working tree state if the user aborted the interactive rebase by
+   emptying the insn sheet.
+
+ * "git show -s" (i.e. show log message only) used to incorrectly emit
+   an extra blank line after a merge commit.
+
+ * "git status", even though it is a read-only operation, tries to
+   update the index with refreshed lstat(2) info to optimize future
+   accesses to the working tree opportunistically, but this could
+   race with a "read-write" operation that modify the index while it
+   is running.  Detect such a race and avoid overwriting the index.
+
+ * "git status" (and "git commit") behaved as if changes in a modified
+   submodule are not there if submodule.*.ignore configuration is set,
+   which was misleading.  The configuration is only to unclutter diff
+   output during the course of development, and should not to hide
+   changes in the "status" output to cause the users forget to commit
+   them.
+
+ * The mode to run tests with HTTP server tests disabled was broken.
diff --git a/Documentation/RelNotes/2.0.2.txt b/Documentation/RelNotes/2.0.2.txt
new file mode 100644 (file)
index 0000000..c48c0aa
--- /dev/null
@@ -0,0 +1,13 @@
+Git v2.0.2 Release Notes
+========================
+
+ * Documentation for "git submodule sync" forgot to say that the subcommand
+   can take the "--recursive" option.
+
+ * Mishandling of patterns in .gitignore that has trailing SPs quoted
+   with backslashes (e.g. ones that end with "\ ") have been
+   corrected.
+
+ * Recent updates to "git repack" started to duplicate objects that
+   are in packfiles marked with .keep flag into the new packfile by
+   mistake.
index 17360e5d535dd7c472d07a7dd13b729a22159f17..3b65461c0b5a4384fbe28dfbd0e3a30e9a9a533a 100644 (file)
@@ -37,11 +37,14 @@ UI, Workflows & Features
    shorter than a page).
 
  * The logic and data used to compute the display width needed for
-   UTF-8 strings have been updated to match Unicode 6.3 better.
+   UTF-8 strings have been updated to match Unicode 7.0 better.
 
  * HTTP-based transports learned to propagate the error messages from
    the webserver better to the client coming over the HTTP transport.
 
+ * The completion script for bash (in contrib/) has been updated to
+   handle aliases that define complex sequence of commands better.
+
  * The "core.preloadindex" configuration variable is by default
    enabled, allowing modern platforms to take advantage of the
    multiple cores they have.
@@ -92,6 +95,10 @@ UI, Workflows & Features
 
  * "git replace" learned the "--edit" subcommand.
 
+ * "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
+   when emitting later input files.
+
  * "git svn" learned to cope with malformed timestamps with only one
    digit in the hour part, e.g. 2014-01-07T5:01:02.048176Z, emitted
    by some broken subversion server implementations.
@@ -99,11 +106,27 @@ 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 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.
+
 
 Performance, Internal Implementation, etc.
 
  * Build procedure for 'subtree' (in contrib/) has been cleaned up.
 
+ * Effort to shrink the size of patches Windows folks maintain on top
+   by upstreaming them continues.
+
+ * Patches maintained by msysgit folks for Windows port are being
+   upstreamed here a bit by bit.
+
+ * The leaf function to check validity of a refname format has been
+   micro-optimized, using SSE2 instructions when available.  A few
+   breakages during its development have been caught and fixed already
+   but there might remain some more still; please test and report if
+   you find any.
+
  * The `core.deltabasecachelimit` used to default to 16 MiB , but this
    proved to be too small, and has been bumped to 96 MiB.
 
@@ -147,13 +170,22 @@ notes for details).
  * Mishandling of patterns in .gitignore that has trailing SPs quoted
    with backslashes (e.g. ones that end with "\ ") have been
    corrected.
-   (merge e61a6c1 pb/trim-trailing-spaces later to maint).
+   (merge 97c1364be6b pb/trim-trailing-spaces later to maint).
 
  * Reworded the error message given upon a failure to open an existing
    loose object file due to e.g. permission issues; it was reported as
    the object being corrupt, but that is not quite true.
    (merge d6c8a05 jk/report-fail-to-read-objects-better later to maint).
 
+ * "git log -2master" is a common typo that shows two commits starting
+   from whichever random branch that is not 'master' that happens to
+   be checked out currently.
+   (merge e3fa568 jc/revision-dash-count-parsing later to maint).
+
+ * 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).
+
  * 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
    in the example) with padding to column-align the output, but did
@@ -161,6 +193,17 @@ notes for details).
    columns are different.
    (merge 7d50987 as/pretty-truncate later to maint).
 
+ * "%G" (nothing after G) is an invalid pretty format specifier, but
+   the parser did not notice it as garbage.
+   (merge 958b2eb jk/pretty-G-format-fixes later to maint).
+
+ * 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.
+   (merge 218aa3a jk/commit-buffer-length later to maint).
+
  * The "mailmap.file" configuration option did not support the tilde
    expansion (i.e. ~user/path and ~/path).
    (merge 9352fd5 ow/config-mailmap-pathname later to maint).
@@ -185,6 +228,11 @@ notes for details).
    line endings.
    (merge 4d4813a bc/blame-crlf-test later to maint).
 
+ * "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().
+   (merge 60a5f5f jc/fix-clone-single-starting-at-a-tag later to maint).
+
  * "git commit --allow-empty-messag -C $commit" did not work when the
    commit did not have any log message.
    (merge 076cbd6 jk/commit-C-pick-empty later to maint).
@@ -233,6 +281,11 @@ notes for details).
    from scratch anyway.
    (merge fb79947 rs/pack-objects-no-unnecessary-realloc later to maint).
 
+ * Recent updates to "git repack" started to duplicate objects that
+   are in packfiles marked with .keep flag into the new packfile by
+   mistake.
+   (merge d078d85 jk/repack-pack-keep-objects later to maint).
+
  * "git rerere forget" did not work well when merge.conflictstyle
    was set to a non-default value.
    (merge de3d8bb fc/rerere-conflict-style later to maint).
@@ -253,6 +306,10 @@ notes for details).
    emptying the insn sheet.
    (merge ddb5432 rr/rebase-autostash-fix later to maint).
 
+ * 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).
+
  * "git show -s" (i.e. show log message only) used to incorrectly emit
    an extra blank line after a merge commit.
    (merge ad2f725 mk/show-s-no-extra-blank-line-for-merges later to maint).
@@ -272,6 +329,10 @@ notes for details).
    them.
    (merge c215d3d jl/status-added-submodule-is-never-ignored later to maint).
 
+ * Documentation for "git submodule sync" forgot to say that the subcommand
+   can take the "--recursive" option.
+   (merge 9393ae7 mc/doc-submodule-sync-recurse later to maint).
+
  * "git update-index --cacheinfo" in 2.0 release crashed on a
    malformed command line.
    (merge c8e1ee4 jc/rev-parse-argh-dashed-multi-words later to maint).
index 9f467d3820a40d1281d29707e98fa16c2bbde63f..1d718bdb9662735d9b446033607c5f2aa92f8a78 100644 (file)
@@ -1905,12 +1905,7 @@ pack.useBitmaps::
        you are debugging pack bitmaps.
 
 pack.writebitmaps::
-       When true, git will write a bitmap index when packing all
-       objects to disk (e.g., when `git repack -a` is run).  This
-       index can speed up the "counting objects" phase of subsequent
-       packs created for clones and fetches, at the cost of some disk
-       space and extra time spent on the initial repack.  Defaults to
-       false.
+       This is a deprecated synonym for `repack.writeBitmaps`.
 
 pack.writeBitmapHashCache::
        When true, git will include a "hash cache" section in the bitmap
@@ -2187,7 +2182,15 @@ repack.packKeptObjects::
        `--pack-kept-objects` was passed. See linkgit:git-repack[1] for
        details. Defaults to `false` normally, but `true` if a bitmap
        index is being written (either via `--write-bitmap-index` or
-       `pack.writeBitmaps`).
+       `repack.writeBitmaps`).
+
+repack.writeBitmaps::
+       When true, git will write a bitmap index when packing all
+       objects to disk (e.g., when `git repack -a` is run).  This
+       index can speed up the "counting objects" phase of subsequent
+       packs created for clones and fetches, at the cost of some disk
+       space and extra time spent on the initial repack.  Defaults to
+       false.
 
 rerere.autoupdate::
        When set to true, `git-rerere` updates the index with the
index 92c68c3fdabafd9ac5afe74256dbd2d56218c247..b09a783ee313bf67cb07db03bb3127d252ab70c3 100644 (file)
@@ -72,6 +72,14 @@ endif::git-pull[]
        setting. See linkgit:git-config[1].
 
 ifndef::git-pull[]
+--refmap=<refspec>::
+       When fetching refs listed on the command line, use the
+       specified refspec (can be given more than once) to map the
+       refs to remote-tracking branches, instead of the values of
+       `remote.*.fetch` configuration variables for the remote
+       repository.  See section on "Configured Remote-tracking
+       Branches" for details.
+
 -t::
 --tags::
        Fetch all tags from the remote (i.e., fetch remote tags
index 5809aa4eb94e969bf700806b1ef3df504ebcfd2b..8deb61469d9cbd8786c27567851158c90c31aa8a 100644 (file)
@@ -17,22 +17,20 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Fetches named heads or tags from one or more other repositories,
-along with the objects necessary to complete them.
+Fetch branches and/or tags (collectively, "refs") from one or more
+other repositories, along with the objects necessary to complete their
+histories.  Remote-tracking branches are updated (see the description
+of <refspec> below for ways to control this behavior).
 
-The ref names and their object names of fetched refs are stored
-in `.git/FETCH_HEAD`.  This information is left for a later merge
-operation done by 'git merge'.
-
-By default, tags are auto-followed.  This means that when fetching
-from a remote, any tags on the remote that point to objects that exist
-in the local repository are fetched.  The effect is to fetch tags that
+By default, any tag that points into the histories being fetched is
+also fetched; the effect is to fetch tags that
 point at branches that you are interested in.  This default behavior
-can be changed by using the --tags or --no-tags options, by
-configuring remote.<name>.tagopt, or by using a refspec that fetches
-tags explicitly.
+can be changed by using the --tags or --no-tags options or by
+configuring remote.<name>.tagopt.  By using a refspec that fetches tags
+explicitly, you can fetch tags that do not point into branches you
+are interested in as well.
 
-'git fetch' can fetch from either a single named repository,
+'git fetch' can fetch from either a single named repository or URL,
 or from several repositories at once if <group> is given and
 there is a remotes.<group> entry in the configuration file.
 (See linkgit:git-config[1]).
@@ -40,6 +38,10 @@ there is a remotes.<group> entry in the configuration file.
 When no remote is specified, by default the `origin` remote will be used,
 unless there's an upstream branch configured for the current branch.
 
+The names of refs that are fetched, together with the object names
+they point at, are written to `.git/FETCH_HEAD`.  This information
+may be used by scripts or other git commands, such as linkgit:git-pull[1].
+
 OPTIONS
 -------
 include::fetch-options.txt[]
@@ -49,6 +51,55 @@ include::pull-fetch-param.txt[]
 include::urls-remotes.txt[]
 
 
+CONFIGURED REMOTE-TRACKING BRANCHES[[CRTB]]
+-------------------------------------------
+
+You often interact with the same remote repository by
+regularly and repeatedly fetching from it.  In order to keep track
+of the progress of such a remote repository, `git fetch` allows you
+to configure `remote.<repository>.fetch` configuration variables.
+
+Typically such a variable may look like this:
+
+------------------------------------------------
+[remote "origin"]
+       fetch = +refs/heads/*:refs/remotes/origin/*
+------------------------------------------------
+
+This configuration is used in two ways:
+
+* When `git fetch` is run without specifying what branches
+  and/or tags to fetch on the command line, e.g. `git fetch origin`
+  or `git fetch`, `remote.<repository>.fetch` values are used as
+  the refspecs---they specify which refs to fetch and which local refs
+  to update.  The example above will fetch
+  all branches that exist in the `origin` (i.e. any ref that matches
+  the left-hand side of the value, `refs/heads/*`) and update the
+  corresponding remote-tracking branches in the `refs/remotes/origin/*`
+  hierarchy.
+
+* When `git fetch` is run with explicit branches and/or tags
+  to fetch on the command line, e.g. `git fetch origin master`, the
+  <refspec>s given on the command line determine what are to be
+  fetched (e.g. `master` in the example,
+  which is a short-hand for `master:`, which in turn means
+  "fetch the 'master' branch but I do not explicitly say what
+  remote-tracking branch to update with it from the command line"),
+  and the example command will
+  fetch _only_ the 'master' branch.  The `remote.<repository>.fetch`
+  values determine which
+  remote-tracking branch, if any, is updated.  When used in this
+  way, the `remote.<repository>.fetch` values do not have any
+  effect in deciding _what_ gets fetched (i.e. the values are not
+  used as refspecs when the command-line lists refspecs); they are
+  only used to decide _where_ the refs that are fetched are stored
+  by acting as a mapping.
+
+The latter use of the `remote.<repository>.fetch` values can be
+overridden by giving the `--refmap=<refspec>` parameter(s) on the
+command line.
+
+
 EXAMPLES
 --------
 
@@ -76,6 +127,19 @@ the local repository by fetching from the branches (respectively)
 The `pu` branch will be updated even if it is does not fast-forward,
 because it is prefixed with a plus sign; `tmp` will not be.
 
+* Peek at a remote's branch, without configuring the remote in your local
+repository:
++
+------------------------------------------------
+$ git fetch git://git.kernel.org/pub/scm/git/git.git maint
+$ git log FETCH_HEAD
+------------------------------------------------
++
+The first command fetches the `maint` branch from the repository at
+`git://git.kernel.org/pub/scm/git/git.git` and the second command uses
+`FETCH_HEAD` to examine the branch with linkgit:git-log[1].  The fetched
+objects will eventually be removed by git's built-in housekeeping (see
+linkgit:git-gc[1]).
 
 BUGS
 ----
index 61461b9f3399bd99547407ae0f3ccb4a38b77582..089dcac0478407a89aea702988adcb4135510180 100644 (file)
@@ -73,6 +73,14 @@ 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.
+
 -l <pattern>::
 --list <pattern>::
        List replace refs for objects that match the given pattern (or
index 987395d22af915832c535840ef59927fc5125294..9bd76a5a6b9327136871dd49cdad5c1ecd8dbcde 100644 (file)
@@ -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 d0fa18aaa8b14f9171800a05742fb1fe326b547b..a60776eb579e10507814c643cc292dfa99750aa9 100644 (file)
@@ -248,6 +248,18 @@ Automating
        cc list. Default is the value of 'sendemail.signedoffbycc' configuration
        value; if that is unspecified, default to --signed-off-by-cc.
 
+--[no-]cc-cover::
+       If this is set, emails found in Cc: headers in the first patch of
+       the series (typically the cover letter) are added to the cc list
+       for each email set. Default is the value of 'sendemail.cccover'
+       configuration value; if that is unspecified, default to --no-cc-cover.
+
+--[no-]to-cover::
+       If this is set, emails found in To: headers in the first patch of
+       the series (typically the cover letter) are added to the to list
+       for each email set. Default is the value of 'sendemail.tocover'
+       configuration value; if that is unspecified, default to --no-to-cover.
+
 --suppress-cc=<category>::
        Specify an additional category of recipients to suppress the
        auto-cc of:
index 89c4d3e39474ed601255371c30b98bdd569f671d..8e6af65da0e0f234315c42391a8bab42fc39abb6 100644 (file)
@@ -20,7 +20,7 @@ SYNOPSIS
 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
              [commit] [--] [<path>...]
 'git submodule' [--quiet] foreach [--recursive] <command>
-'git submodule' [--quiet] sync [--] [<path>...]
+'git submodule' [--quiet] sync [--recursive] [--] [<path>...]
 
 
 DESCRIPTION
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.
 
diff --git a/Documentation/git-verify-commit.txt b/Documentation/git-verify-commit.txt
new file mode 100644 (file)
index 0000000..9413e28
--- /dev/null
@@ -0,0 +1,28 @@
+git-verify-commit(1)
+====================
+
+NAME
+----
+git-verify-commit - Check the GPG signature of commits
+
+SYNOPSIS
+--------
+[verse]
+'git verify-commit' <commit>...
+
+DESCRIPTION
+-----------
+Validates the gpg signature created by 'git commit -S'.
+
+OPTIONS
+-------
+-v::
+--verbose::
+       Print the contents of the commit object before validating it.
+
+<commit>...::
+       SHA-1 identifiers of Git commit objects.
+
+GIT
+---
+Part of the linkgit:git[1] suite
index 3bd68b016791ac082e2a556a4835b22ba241b92f..79242096714e90a385e9b63a60326a41f8bd983d 100644 (file)
@@ -43,9 +43,10 @@ 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.0/git.html[documentation for release 2.0]
+* link:v2.0.1/git.html[documentation for release 2.0.1]
 
 * release notes for
+  link:RelNotes/2.0.1.txt[2.0.1],
   link:RelNotes/2.0.0.txt[2.0.0].
 
 * link:v1.9.4/git.html[documentation for release 1.9.4]
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 18cffc25b809fa3695b0cc60c732d058e8843ed6..1ebbf1d738403204b77a6b18547de25e94ab28be 100644 (file)
@@ -12,9 +12,23 @@ ifndef::git-pull[]
 endif::git-pull[]
 
 <refspec>::
-       The format of a <refspec> parameter is an optional plus
-       `+`, followed by the source ref <src>, followed
-       by a colon `:`, followed by the destination ref <dst>.
+       Specifies which refs to fetch and which local refs to update.
+       When no <refspec>s appear on the command line, the refs to fetch
+       are read from `remote.<repository>.fetch` variables instead
+ifndef::git-pull[]
+       (see <<CRTB,CONFIGURED REMOTE-TRACKING BRANCHES>> below).
+endif::git-pull[]
+ifdef::git-pull[]
+       (see linkgit:git-fetch[1]).
+endif::git-pull[]
++
+The format of a <refspec> parameter is an optional plus
+`+`, followed by the source ref <src>, followed
+by a colon `:`, followed by the destination ref <dst>.
+The colon can be omitted when <dst> is empty.
++
+`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
+it requests fetching everything up to the given tag.
 +
 The remote ref that matches <src>
 is fetched, and if <dst> is not empty string, the local
@@ -24,55 +38,34 @@ is updated even if it does not result in a fast-forward
 update.
 +
 [NOTE]
-If the remote branch from which you want to pull is
-modified in non-linear ways such as being rewound and
-rebased frequently, then a pull will attempt a merge with
-an older version of itself, likely conflict, and fail.
-It is under these conditions that you would want to use
-the `+` sign to indicate non-fast-forward updates will
-be needed.  There is currently no easy way to determine
-or declare that a branch will be made available in a
-repository with this behavior; the pulling user simply
+When the remote branch you want to fetch is known to
+be rewound and rebased regularly, it is expected that
+its new tip will not be descendant of its previous tip
+(as stored in your remote-tracking branch the last time
+you fetched).  You would want
+to use the `+` sign to indicate non-fast-forward updates
+will be needed for such branches.  There is no way to
+determine or declare that a branch will be made available
+in a repository with this behavior; the pulling user simply
 must know this is the expected usage pattern for a branch.
-+
-[NOTE]
-You never do your own development on branches that appear
-on the right hand side of a <refspec> colon on `Pull:` lines;
-they are to be updated by 'git fetch'.  If you intend to do
-development derived from a remote branch `B`, have a `Pull:`
-line to track it (i.e. `Pull: B:remote-B`), and have a separate
-branch `my-B` to do your development on top of it.  The latter
-is created by `git branch my-B remote-B` (or its equivalent `git
-checkout -b my-B remote-B`).  Run `git fetch` to keep track of
-the progress of the remote side, and when you see something new
-on the remote branch, merge it into your development branch with
-`git pull . remote-B`, while you are on `my-B` branch.
+ifdef::git-pull[]
 +
 [NOTE]
 There is a difference between listing multiple <refspec>
 directly on 'git pull' command line and having multiple
-`Pull:` <refspec> lines for a <repository> and running
+`remote.<repository>.fetch` entries in your configuration
+for a <repository> and running a
 'git pull' command without any explicit <refspec> parameters.
-<refspec> listed explicitly on the command line are always
+<refspec>s listed explicitly on the command line are always
 merged into the current branch after fetching.  In other words,
-if you list more than one remote refs, you would be making
-an Octopus.  While 'git pull' run without any explicit <refspec>
-parameter takes default <refspec>s from `Pull:` lines, it
-merges only the first <refspec> found into the current branch,
-after fetching all the remote refs.  This is because making an
+if you list more than one remote ref, 'git pull' will create
+an Octopus merge.  On the other hand, if you do not list any
+explicit <refspec> parameter on the command line, 'git pull'
+will fetch all the <refspec>s it finds in the
+`remote.<repository>.fetch` configuration and merge
+only the first <refspec> found into the current branch.
+This is because making an
 Octopus from remote refs is rarely done, while keeping track
 of multiple remote heads in one-go by fetching more than one
 is often useful.
-+
-Some short-cut notations are also supported.
-+
-* `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
-  it requests fetching everything up to the given tag.
-ifndef::git-pull[]
-* A parameter <ref> without a colon fetches that ref into FETCH_HEAD,
-endif::git-pull[]
-ifdef::git-pull[]
-* A parameter <ref> without a colon merges <ref> into the current
-  branch,
 endif::git-pull[]
-  and updates the remote-tracking branches (if any).
index 077a7096a4ba3c47a0cbc8981abdde1b7b35d924..f9c06a7573e9294a967d641cdfc2723b86c1c864 100644 (file)
@@ -7,10 +7,10 @@ use the mem* functions than a str* one (memchr vs. strchr e.g.).
 Though, one has to be careful about the fact that str* functions often
 stop on NULs and that strbufs may have embedded NULs.
 
-An strbuf is NUL terminated for convenience, but no function in the
+A strbuf is NUL terminated for convenience, but no function in the
 strbuf API actually relies on the string being free of NULs.
 
-strbufs has some invariants that are very important to keep in mind:
+strbufs have some invariants that are very important to keep in mind:
 
 . The `buf` member is never NULL, so it can be used in any usual C
 string operations safely. strbuf's _have_ to be initialized either by
@@ -56,8 +56,8 @@ Data structures
 * `struct strbuf`
 
 This is the string buffer structure. The `len` member can be used to
-determine the current length of the string, and `buf` member provides access to
-the string itself.
+determine the current length of the string, and `buf` member provides
+access to the string itself.
 
 Functions
 ---------
@@ -202,7 +202,7 @@ strbuf_addstr(sb, "immediate string");
 
 `strbuf_addbuf`::
 
-       Copy the contents of an other buffer at the end of the current one.
+       Copy the contents of another buffer at the end of the current one.
 
 `strbuf_adddup`::
 
index 59be59b0ebba8c24875643521c9135ab907a33ce..229f845dfa67b9c2e74b2eb1de80a224ea757b53 100644 (file)
@@ -60,7 +60,7 @@ Because Git repositories are accessed by standard path components
 server administrators MAY use directory based permissions within
 their HTTP server to control repository access.
 
-Clients SHOULD support Basic authentication as described by RFC 2616.
+Clients SHOULD support Basic authentication as described by RFC 2617.
 Servers SHOULD support Basic authentication by relying upon the
 HTTP server placed in front of the Git server software.
 
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 07ea1058379ab963648d0df7fd2917e0d2efa8a7..840057cba7ebd5fea25d84af367e49f0fcfc41c7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -552,6 +552,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 +876,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
@@ -999,6 +1001,7 @@ BUILTIN_OBJS += builtin/update-ref.o
 BUILTIN_OBJS += builtin/update-server-info.o
 BUILTIN_OBJS += builtin/upload-archive.o
 BUILTIN_OBJS += builtin/var.o
+BUILTIN_OBJS += builtin/verify-commit.o
 BUILTIN_OBJS += builtin/verify-pack.o
 BUILTIN_OBJS += builtin/verify-tag.o
 BUILTIN_OBJS += builtin/write-tree.o
diff --git a/alloc.c b/alloc.c
index f3ee745695643c00f175288a9592ab073dd589b0..eb22a45c9d0acf08a6024094bd0a2090a7307945 100644 (file)
--- a/alloc.c
+++ b/alloc.c
@@ -47,23 +47,32 @@ union any_object {
 
 DEFINE_ALLOCATOR(blob, struct blob)
 DEFINE_ALLOCATOR(tree, struct tree)
-DEFINE_ALLOCATOR(commit, struct commit)
+DEFINE_ALLOCATOR(raw_commit, struct commit)
 DEFINE_ALLOCATOR(tag, struct tag)
 DEFINE_ALLOCATOR(object, union any_object)
 
+void *alloc_commit_node(void)
+{
+       static int commit_count;
+       struct commit *c = alloc_raw_commit_node();
+       c->index = commit_count++;
+       return c;
+}
+
 static void report(const char *name, unsigned int count, size_t size)
 {
        fprintf(stderr, "%10s: %8u (%"PRIuMAX" kB)\n",
                        name, count, (uintmax_t) size);
 }
 
-#define REPORT(name)   \
-    report(#name, name##_allocs, name##_allocs * sizeof(struct name) >> 10)
+#define REPORT(name, type)     \
+    report(#name, name##_allocs, name##_allocs * sizeof(type) >> 10)
 
 void alloc_report(void)
 {
-       REPORT(blob);
-       REPORT(tree);
-       REPORT(commit);
-       REPORT(tag);
+       REPORT(blob, struct blob);
+       REPORT(tree, struct tree);
+       REPORT(raw_commit, struct commit);
+       REPORT(tag, struct tag);
+       REPORT(object, union any_object);
 }
index c47c110e0f18f3183d54c9afb63cffef686a21db..5d91f31ca25e0c16fca0e8c23d83706840bf6c57 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -128,6 +128,7 @@ extern int cmd_update_server_info(int argc, const char **argv, const char *prefi
 extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
 extern int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
 extern int cmd_var(int argc, const char **argv, const char *prefix);
+extern int cmd_verify_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
 extern int cmd_version(int argc, const char **argv, const char *prefix);
 extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
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 bc924ab2d03a17477fbbe9ea6a25a704963f85b5..5fd099ed40859ddfd838ea99191921d63e6eed12 100644 (file)
@@ -1281,9 +1281,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
         */
        patch->def_name = git_header_name(line, len);
        if (patch->def_name && root) {
-               char *s = xmalloc(root_len + strlen(patch->def_name) + 1);
-               strcpy(s, root);
-               strcpy(s + root_len, patch->def_name);
+               char *s = xstrfmt("%s%s", root, patch->def_name);
                free(patch->def_name);
                patch->def_name = s;
        }
@@ -3086,13 +3084,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;
@@ -3259,7 +3259,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))
@@ -3413,7 +3413,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))
@@ -3646,7 +3646,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...
@@ -3684,8 +3684,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);
@@ -4504,8 +4504,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 a52a279144bec14f2b2a0795f30e0c5871f60c1b..c59e70202175fc89992c23b48ea5f0738664a2f6 100644 (file)
@@ -1655,7 +1655,7 @@ static void get_commit_info(struct commit *commit,
 {
        int len;
        const char *subject, *encoding;
-       char *message;
+       const char *message;
 
        commit_info_init(ret);
 
@@ -1666,7 +1666,7 @@ static void get_commit_info(struct commit *commit,
                    &ret->author_time, &ret->author_tz);
 
        if (!detailed) {
-               logmsg_free(message, commit);
+               unuse_commit_buffer(commit, message);
                return;
        }
 
@@ -1680,7 +1680,7 @@ static void get_commit_info(struct commit *commit,
        else
                strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
 
-       logmsg_free(message, commit);
+       unuse_commit_buffer(commit, message);
 }
 
 /*
@@ -2008,6 +2008,12 @@ static void output(struct scoreboard *sb, int option)
        }
 }
 
+static const char *get_next_line(const char *start, const char *end)
+{
+       const char *nl = memchr(start, '\n', end - start);
+       return nl ? nl + 1 : end;
+}
+
 /*
  * To allow quick access to the contents of nth line in the
  * final image, prepare an index in the scoreboard.
@@ -2019,39 +2025,19 @@ static int prepare_lines(struct scoreboard *sb)
        const char *end = buf + len;
        const char *p;
        int *lineno;
-       int num = 0, incomplete = 0;
+       int num = 0;
 
-       for (p = buf;;) {
-               p = memchr(p, '\n', end - p);
-               if (p) {
-                       p++;
-                       num++;
-                       continue;
-               }
-               break;
-       }
+       for (p = buf; p < end; p = get_next_line(p, end))
+               num++;
 
-       if (len && end[-1] != '\n')
-               incomplete++; /* incomplete line at the end */
+       sb->lineno = lineno = xmalloc(sizeof(*sb->lineno) * (num + 1));
 
-       sb->lineno = xmalloc(sizeof(*sb->lineno) * (num + incomplete + 1));
-       lineno = sb->lineno;
+       for (p = buf; p < end; p = get_next_line(p, end))
+               *lineno++ = p - buf;
 
-       *lineno++ = 0;
-       for (p = buf;;) {
-               p = memchr(p, '\n', end - p);
-               if (p) {
-                       p++;
-                       *lineno++ = p - buf;
-                       continue;
-               }
-               break;
-       }
+       *lineno = len;
 
-       if (incomplete)
-               *lineno++ = len;
-
-       sb->num_lines = num + incomplete;
+       sb->num_lines = num;
        return sb->num_lines;
 }
 
@@ -2265,6 +2251,18 @@ static void append_merge_parents(struct commit_list **tail)
        strbuf_release(&line);
 }
 
+/*
+ * This isn't as simple as passing sb->buf and sb->len, because we
+ * want to transfer ownership of the buffer to the commit (so we
+ * must use detach).
+ */
+static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
+{
+       size_t len;
+       void *buf = strbuf_detach(sb, &len);
+       set_commit_buffer(c, buf, len);
+}
+
 /*
  * Prepare a dummy commit that represents the work tree (or staged) item.
  * Note that annotating work tree item never works in the reverse.
@@ -2286,7 +2284,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        struct strbuf msg = STRBUF_INIT;
 
        time(&now);
-       commit = xcalloc(1, sizeof(*commit));
+       commit = alloc_commit_node();
        commit->object.parsed = 1;
        commit->date = now;
        commit->object.type = OBJ_COMMIT;
@@ -2313,7 +2311,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                    ident, ident, path,
                    (!contents_from ? path :
                     (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
-       commit->buffer = strbuf_detach(&msg, NULL);
+       set_commit_buffer_from_strbuf(commit, &msg);
 
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
@@ -2391,7 +2389,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;
 }
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 9a9151575d3a8d4eb16d73e432835d3cda4374b8..27701d222c78ed84691b65334a7909f90c589464 100644 (file)
@@ -48,7 +48,7 @@ enum color_clean {
        CLEAN_COLOR_PROMPT = 2,
        CLEAN_COLOR_HEADER = 3,
        CLEAN_COLOR_HELP = 4,
-       CLEAN_COLOR_ERROR = 5,
+       CLEAN_COLOR_ERROR = 5
 };
 
 #define MENU_OPTS_SINGLETON            01
index a5b2d9db360eb59d5a73c2e221fed047a94b0615..f0dabecca88d8f16528fb68489e5b7a7700e2a47 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),
@@ -696,7 +695,7 @@ static void write_refspec_config(const char* src_ref_prefix,
        if (option_mirror || !option_bare) {
                if (option_single_branch && !option_mirror) {
                        if (option_branch) {
-                               if (strstr(our_head_points_at->name, "refs/tags/"))
+                               if (starts_with(our_head_points_at->name, "refs/tags/"))
                                        strbuf_addf(&value, "+%s:%s", our_head_points_at->name,
                                                our_head_points_at->name);
                                else
index 987a4c3d731d7cf01f61e4fddec208b4cb51a4af..8a66c74e0fd405a06e6c767d385013ff7150db04 100644 (file)
@@ -123,8 +123,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
                        die_errno("git commit-tree: failed to read");
        }
 
-       if (commit_tree(&buffer, tree_sha1, parents, commit_sha1,
-                       NULL, sign_commit)) {
+       if (commit_tree(buffer.buf, buffer.len, tree_sha1, parents,
+                       commit_sha1, NULL, sign_commit)) {
                strbuf_release(&buffer);
                return 1;
        }
index ec753412381f26b29274805d32524515171bb39d..72eb3beb36cd06f6e41d80d5761a953a1c3a1d56 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);
@@ -432,24 +429,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();
@@ -1744,8 +1739,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                append_merge_tag_headers(parents, &tail);
        }
 
-       if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1,
-                                author_ident.buf, sign_commit, extra)) {
+       if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
+                        parents, sha1, author_ident.buf, sign_commit, extra)) {
                rollback_index_files();
                die(_("failed to write commit object"));
        }
index be6417d166abf428d379a70b9d67f894da4641d1..ce0e019e0caa358e1f5e39e5ed51bec3945197e0 100644 (file)
@@ -22,14 +22,10 @@ static int stdin_diff_commit(struct commit *commit, char *line, int len)
        if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
                /* Graft the fake parents locally to the commit */
                int pos = 41;
-               struct commit_list **pptr, *parents;
+               struct commit_list **pptr;
 
                /* Free the real parent list */
-               for (parents = commit->parents; parents; ) {
-                       struct commit_list *tmp = parents->next;
-                       free(parents);
-                       parents = tmp;
-               }
+               free_commit_list(commit->parents);
                commit->parents = NULL;
                pptr = &(commit->parents);
                while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
index ef4481615fd2f4d60cd3cd6a4bbcceaca93c92d5..92b4624a4b93ff50c661e44242d508aed31877eb 100644 (file)
@@ -282,6 +282,7 @@ static const char *find_encoding(const char *begin, const char *end)
 static void handle_commit(struct commit *commit, struct rev_info *rev)
 {
        int saved_output_format = rev->diffopt.output_format;
+       const char *commit_buffer;
        const char *author, *author_end, *committer, *committer_end;
        const char *encoding, *message;
        char *reencoded = NULL;
@@ -291,7 +292,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
 
        parse_commit_or_die(commit);
-       author = strstr(commit->buffer, "\nauthor ");
+       commit_buffer = get_commit_buffer(commit, NULL);
+       author = strstr(commit_buffer, "\nauthor ");
        if (!author)
                die ("Could not find author in commit %s",
                     sha1_to_hex(commit->object.sha1));
@@ -338,6 +340,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
                          ? strlen(message) : 0),
               reencoded ? reencoded : message ? message : "");
        free(reencoded);
+       unuse_commit_buffer(commit, commit_buffer);
 
        for (i = 0, p = commit->parents; p; p = p->next) {
                int mark = get_object_mark(&p->item->object);
index 55f457c04f5c72ca31ab4fbc28e62ea48b6cb471..e8d0cca3e4110e1ae6db82f21c899c44460cb849 100644 (file)
@@ -45,6 +45,8 @@ static struct transport *gsecondary;
 static const char *submodule_prefix = "";
 static const char *recurse_submodules_default;
 static int shown_url = 0;
+static int refmap_alloc, refmap_nr;
+static const char **refmap_array;
 
 static int option_parse_recurse_submodules(const struct option *opt,
                                   const char *arg, int unset)
@@ -69,6 +71,19 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
        return 0;
 }
 
+static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
+{
+       ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);
+
+       /*
+        * "git fetch --refmap='' origin foo"
+        * can be used to tell the command not to store anywhere
+        */
+       if (*arg)
+               refmap_array[refmap_nr++] = arg;
+       return 0;
+}
+
 static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "all", &all,
@@ -107,6 +122,8 @@ static struct option builtin_fetch_options[] = {
                   N_("default mode for recursion"), PARSE_OPT_HIDDEN },
        OPT_BOOL(0, "update-shallow", &update_shallow,
                 N_("accept refs that update .git/shallow")),
+       { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
+         N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
        OPT_END()
 };
 
@@ -278,6 +295,9 @@ static struct ref *get_ref_map(struct transport *transport,
        const struct ref *remote_refs = transport_get_remote_refs(transport);
 
        if (refspec_count) {
+               struct refspec *fetch_refspec;
+               int fetch_refspec_nr;
+
                for (i = 0; i < refspec_count; i++) {
                        get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
                        if (refspecs[i].dst && refspecs[i].dst[0])
@@ -307,12 +327,21 @@ static struct ref *get_ref_map(struct transport *transport,
                 * by ref_remove_duplicates() in favor of one of these
                 * opportunistic entries with FETCH_HEAD_IGNORE.
                 */
-               for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
-                       get_fetch_map(ref_map, &transport->remote->fetch[i],
-                                     &oref_tail, 1);
+               if (refmap_array) {
+                       fetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array);
+                       fetch_refspec_nr = refmap_nr;
+               } else {
+                       fetch_refspec = transport->remote->fetch;
+                       fetch_refspec_nr = transport->remote->fetch_refspec_nr;
+               }
+
+               for (i = 0; i < fetch_refspec_nr; i++)
+                       get_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);
 
                if (tags == TAGS_SET)
                        get_fetch_map(remote_refs, tag_refspec, &tail, 0);
+       } else if (refmap_array) {
+               die("--refmap option is only meaningful with command-line refspec(s).");
        } else {
                /* Use the defaults */
                struct remote *remote = transport->remote;
@@ -1053,16 +1082,11 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
                refs = xcalloc(argc + 1, sizeof(const char *));
                for (i = 0; i < argc; i++) {
                        if (!strcmp(argv[i], "tag")) {
-                               char *ref;
                                i++;
                                if (i >= argc)
                                        die(_("You need to specify a tag name."));
-                               ref = xmalloc(strlen(argv[i]) * 2 + 22);
-                               strcpy(ref, "refs/tags/");
-                               strcat(ref, argv[i]);
-                               strcat(ref, ":refs/tags/");
-                               strcat(ref, argv[i]);
-                               refs[j++] = ref;
+                               refs[j++] = xstrfmt("refs/tags/%s:refs/tags/%s",
+                                                   argv[i], argv[i]);
                        } else
                                refs[j++] = argv[i];
                }
index ad3bc58c74a2b6927d3604e458e01a14b9834946..79df05ef526bcd1a3be2971c9d467a930001639d 100644 (file)
@@ -178,11 +178,8 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
                int len = strlen(origin);
                if (origin[0] == '\'' && origin[len - 1] == '\'')
                        origin = xmemdupz(origin + 1, len - 2);
-       } else {
-               char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5);
-               sprintf(new_origin, "%s of %s", origin, src);
-               origin = new_origin;
-       }
+       } else
+               origin = xstrfmt("%s of %s", origin, src);
        if (strcmp(".", src))
                origin_data->is_local_branch = 0;
        string_list_append(&origins, origin)->util = origin_data;
@@ -230,12 +227,14 @@ static void add_branch_desc(struct strbuf *out, const char *name)
 static void record_person(int which, struct string_list *people,
                          struct commit *commit)
 {
+       const char *buffer;
        char *name_buf, *name, *name_end;
        struct string_list_item *elem;
        const char *field;
 
        field = (which == 'a') ? "\nauthor " : "\ncommitter ";
-       name = strstr(commit->buffer, field);
+       buffer = get_commit_buffer(commit, NULL);
+       name = strstr(buffer, field);
        if (!name)
                return;
        name += strlen(field);
@@ -247,6 +246,7 @@ static void record_person(int which, struct string_list *people,
        if (name_end < name)
                return;
        name_buf = xmemdupz(name, name_end - name + 1);
+       unuse_commit_buffer(commit, buffer);
 
        elem = string_list_lookup(people, name_buf);
        if (!elem) {
index 3e1d5c3334e50035fdbf835cfdafbb39c0bacfd0..4135980f20786ef3320e334c4001fd67fc169056 100644 (file)
@@ -193,7 +193,7 @@ static int verify_format(const char *format)
                at = parse_atom(sp + 2, ep);
                cp = ep + 1;
 
-               if (!memcmp(used_atom[at], "color:", 6))
+               if (starts_with(used_atom[at], "color:"))
                        need_color_reset_at_eol = !!strcmp(used_atom[at], color_reset);
        }
        return 0;
index fc150c882122c53965fd246ad862e3a75d8768e2..8aadca160e857be8a87ec1f152e51eb6f51c90b5 100644 (file)
@@ -310,8 +310,7 @@ static int fsck_obj(struct object *obj)
        if (obj->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *) obj;
 
-               free(commit->buffer);
-               commit->buffer = NULL;
+               free_commit_buffer(commit);
 
                if (!commit->parents && show_root)
                        printf("root %s\n", sha1_to_hex(commit->object.sha1));
index 18f57de58b02de33909a40ea9720e6602cc9dcb5..fc40411892f13da7e739bc4801bbace3021c0e2b 100644 (file)
@@ -786,7 +786,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
                        }
                        if (obj->type == OBJ_COMMIT) {
                                struct commit *commit = (struct commit *) obj;
-                               commit->buffer = NULL;
+                               if (detach_commit_buffer(commit, NULL) != data)
+                                       die("BUG: parse_object_buffer transmogrified our buffer");
                        }
                        obj->flags |= FLAG_CHECKED;
                }
@@ -1505,7 +1506,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];
@@ -1602,24 +1604,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)
@@ -1667,8 +1667,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 0f59c25d362d8238786a884f0a154f29bd97d767..27c1b65db46bb1bead9045b7b6be08a1ee199835 100644 (file)
@@ -347,8 +347,7 @@ static int cmd_log_walk(struct rev_info *rev)
                        rev->max_count++;
                if (!rev->reflog_info) {
                        /* we allow cycles in reflog ancestry */
-                       free(commit->buffer);
-                       commit->buffer = NULL;
+                       free_commit_buffer(commit);
                }
                free_commit_list(commit->parents);
                commit->parents = NULL;
@@ -925,9 +924,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
                                &need_8bit_cte);
 
-       for (i = 0; !need_8bit_cte && i < nr; i++)
-               if (has_non_ascii(list[i]->buffer))
+       for (i = 0; !need_8bit_cte && i < nr; i++) {
+               const char *buf = get_commit_buffer(list[i], NULL);
+               if (has_non_ascii(buf))
                        need_8bit_cte = 1;
+               unuse_commit_buffer(list[i], buf);
+       }
 
        if (!branch_name)
                branch_name = find_branch_name(rev);
@@ -1528,8 +1530,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                    reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
-               free(commit->buffer);
-               commit->buffer = NULL;
+               free_commit_buffer(commit);
 
                /* We put one extra blank line between formatted
                 * patches and this flag is used by log-tree code
index 428ca247bd17e0f18776b46d40a09282b0b955de..22491a1c2b906f4e4ae607cda8925b1254b202b3 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;
@@ -852,8 +848,8 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
        prepare_to_commit(remoteheads);
-       if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
-                       sign_commit))
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parent,
+                       result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        finish(head, remoteheads, result_commit, "In-index merge");
        drop_save();
@@ -877,8 +873,8 @@ static int finish_automerge(struct commit *head,
                commit_list_insert(head, &parents);
        strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
-       if (commit_tree(&merge_msg, result_tree, parents, result_commit,
-                       NULL, sign_commit))
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
+                       result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
        finish(head, remoteheads, result_commit, buf.buf);
@@ -1282,10 +1278,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                                printf(_("Commit %s has a good GPG signature by %s\n"),
                                       hex, signature_check.signer);
 
-                       free(signature_check.gpg_output);
-                       free(signature_check.gpg_status);
-                       free(signature_check.signer);
-                       free(signature_check.key);
+                       signature_check_clear(&signature_check);
                }
        }
 
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 c824d4ec5f1c076e4833c690dad378125dde6da9..3c8f319be675d14a6ee60d303335190b7bdce9c8 100644 (file)
@@ -33,10 +33,7 @@ static void name_rev(struct commit *commit,
                return;
 
        if (deref) {
-               char *new_name = xmalloc(strlen(tip_name)+3);
-               strcpy(new_name, tip_name);
-               strcat(new_name, "^0");
-               tip_name = new_name;
+               tip_name = xstrfmt("%s^0", tip_name);
 
                if (generation)
                        die("generation: %d, but deref?", generation);
index de36c60ca11d248013c2de42d196f1aff8a2937b..238b5021eb105007a42061d5f8febc455530da45 100644 (file)
@@ -2214,10 +2214,6 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                cache_max_small_delta_size = git_config_int(k, v);
                return 0;
        }
-       if (!strcmp(k, "pack.writebitmaps")) {
-               write_bitmap_index = git_config_bool(k, v);
-               return 0;
-       }
        if (!strcmp(k, "pack.writebitmaphashcache")) {
                if (git_config_bool(k, v))
                        write_bitmap_options |= BITMAP_OPT_HASH_CACHE;
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 c3230817db4a7676eb74335b254f30597e66edd9..18458e81c6351d53afe1a50efff183889e5ab81b 100644 (file)
@@ -614,12 +614,9 @@ static void run_update_post_hook(struct command *commands)
        argv[0] = hook;
 
        for (argc = 1, cmd = commands; cmd; cmd = cmd->next) {
-               char *p;
                if (cmd->error_string || cmd->did_not_exist)
                        continue;
-               p = xmalloc(strlen(cmd->ref_name) + 1);
-               strcpy(p, cmd->ref_name);
-               argv[argc] = p;
+               argv[argc] = xstrdup(cmd->ref_name);
                argc++;
        }
        argv[argc] = NULL;
index a8efe3da4d7dad3b17582c91671f0f475d54a9ac..8e1dc39162681503505c9c3897e046514ce2e6a8 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;
index 6b0b62dcb2687e78ec433e7de1103f0c0f74a1fc..a77e743b94036b2d856e6b3f74999170e39b5f17 100644 (file)
@@ -10,6 +10,7 @@
 
 static int delta_base_offset = 1;
 static int pack_kept_objects = -1;
+static int write_bitmaps;
 static char *packdir, *packtmp;
 
 static const char *const git_repack_usage[] = {
@@ -27,6 +28,11 @@ static int repack_config(const char *var, const char *value, void *cb)
                pack_kept_objects = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "repack.writebitmaps") ||
+           !strcmp(var, "pack.writebitmaps")) {
+               write_bitmaps = git_config_bool(var, value);
+               return 0;
+       }
        return git_default_config(var, value, cb);
 }
 
@@ -77,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)))
@@ -149,7 +154,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        int no_update_server_info = 0;
        int quiet = 0;
        int local = 0;
-       int write_bitmap = -1;
 
        struct option builtin_repack_options[] = {
                OPT_BIT('a', NULL, &pack_everything,
@@ -168,7 +172,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                OPT__QUIET(&quiet, N_("be quiet")),
                OPT_BOOL('l', "local", &local,
                                N_("pass --local to git-pack-objects")),
-               OPT_BOOL('b', "write-bitmap-index", &write_bitmap,
+               OPT_BOOL('b', "write-bitmap-index", &write_bitmaps,
                                N_("write bitmap index")),
                OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
                                N_("with -A, do not loosen objects older than this")),
@@ -191,7 +195,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                                git_repack_usage, 0);
 
        if (pack_kept_objects < 0)
-               pack_kept_objects = write_bitmap;
+               pack_kept_objects = write_bitmaps;
 
        packdir = mkpathdup("%s/pack", get_object_directory());
        packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid());
@@ -217,9 +221,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                argv_array_pushf(&cmd_args, "--no-reuse-delta");
        if (no_reuse_object)
                argv_array_pushf(&cmd_args, "--no-reuse-object");
-       if (write_bitmap >= 0)
-               argv_array_pushf(&cmd_args, "--%swrite-bitmap-index",
-                                write_bitmap ? "" : "no-");
+       if (write_bitmaps)
+               argv_array_push(&cmd_args, "--write-bitmap-index");
 
        if (pack_everything & ALL_INTO_ONE) {
                get_non_kept_pack_filenames(&existing_packs);
index 1bb491d3c4f282e7f68d25cae0e00f419df1dd93..d1ea2c2e5606f2091efb487dc1599ec0a9116a6e 100644 (file)
@@ -23,9 +23,9 @@ static const char * const git_replace_usage[] = {
 };
 
 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 +188,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 +222,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 +230,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 +270,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 +286,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);
 
@@ -297,6 +302,7 @@ static int edit_and_replace(const char *object_ref, int 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,
@@ -310,6 +316,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
                OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
                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()
        };
@@ -329,6 +336,10 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                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 +357,7 @@ 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_LIST:
                if (argc > 1)
index f368266762fae1e6c783d7351e289e423e408b3c..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;
@@ -93,7 +93,7 @@ static int reset_index(const unsigned char *sha1, int reset_type, int quiet)
 static void print_new_head_line(struct commit *commit)
 {
        const char *hex, *body;
-       char *msg;
+       const char *msg;
 
        hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        printf(_("HEAD is now at %s"), hex);
@@ -109,7 +109,7 @@ static void print_new_head_line(struct commit *commit)
        }
        else
                printf("\n");
-       logmsg_free(msg, commit);
+       unuse_commit_buffer(commit, msg);
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
@@ -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 9f92905379d1801a35bfad87bc05002d76dc23f4..ff84a825ff776abc01087f37782223ee26a1af7a 100644 (file)
@@ -106,7 +106,7 @@ static void show_commit(struct commit *commit, void *data)
        else
                putchar('\n');
 
-       if (revs->verbose_header && commit->buffer) {
+       if (revs->verbose_header && get_cached_commit_buffer(commit, NULL)) {
                struct strbuf buf = STRBUF_INIT;
                struct pretty_print_context ctx = {0};
                ctx.abbrev = revs->abbrev;
@@ -173,8 +173,7 @@ static void finish_commit(struct commit *commit, void *data)
                free_commit_list(commit->parents);
                commit->parents = NULL;
        }
-       free(commit->buffer);
-       commit->buffer = NULL;
+       free_commit_buffer(commit);
 }
 
 static void finish_object(struct object *obj,
index 1a6122d3ae29609c30c26959fbb1ff799e13a4b2..8102aaa9248d55055761f9899903cac2a2c7cc1e 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
@@ -775,6 +776,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 d87317290c0bbfffb7ac66fb7cff5c608f092d93..5fd4e4e48839a9dc2998d3fba0e48008b902b12a 100644 (file)
@@ -755,7 +755,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                }
 
                for (i = 0; i < reflog; i++) {
-                       char *logmsg, *m;
+                       char *logmsg;
                        const char *msg;
                        unsigned long timestamp;
                        int tz;
@@ -770,11 +770,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                                msg = "(none)";
                        else
                                msg++;
-                       m = xmalloc(strlen(msg) + 200);
-                       sprintf(m, "(%s) %s",
-                               show_date(timestamp, tz, 1),
-                               msg);
-                       reflog_msg[i] = m;
+                       reflog_msg[i] = xstrfmt("(%s) %s",
+                                               show_date(timestamp, tz, 1),
+                                               msg);
                        free(logmsg);
                        sprintf(nth_desc, "%s@{%d}", *av, base+i);
                        append_ref(nth_desc, sha1, 1);
index 1101c19596c549e50b2e2b6c640a5e318cefb802..9d7643f127e7c0ea965ebf599574749c950d769f 100644 (file)
@@ -83,7 +83,7 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
 enum contains_result {
        CONTAINS_UNKNOWN = -1,
        CONTAINS_NO = 0,
-       CONTAINS_YES = 1,
+       CONTAINS_YES = 1
 };
 
 /*
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");
        }
 
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
new file mode 100644 (file)
index 0000000..b0f8504
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Builtin "git commit-commit"
+ *
+ * Copyright (c) 2014 Michael J Gruber <git@drmicha.warpmail.net>
+ *
+ * Based on git-verify-tag
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "commit.h"
+#include "run-command.h"
+#include <signal.h>
+#include "parse-options.h"
+#include "gpg-interface.h"
+
+static const char * const verify_commit_usage[] = {
+               N_("git verify-commit [-v|--verbose] <commit>..."),
+               NULL
+};
+
+static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, int verbose)
+{
+       struct signature_check signature_check;
+
+       memset(&signature_check, 0, sizeof(signature_check));
+
+       check_commit_signature(lookup_commit(sha1), &signature_check);
+
+       if (verbose && signature_check.payload)
+               fputs(signature_check.payload, stdout);
+
+       if (signature_check.gpg_output)
+               fputs(signature_check.gpg_output, stderr);
+
+       signature_check_clear(&signature_check);
+       return signature_check.result != 'G';
+}
+
+static int verify_commit(const char *name, int verbose)
+{
+       enum object_type type;
+       unsigned char sha1[20];
+       char *buf;
+       unsigned long size;
+       int ret;
+
+       if (get_sha1(name, sha1))
+               return error("commit '%s' not found.", name);
+
+       buf = read_sha1_file(sha1, &type, &size);
+       if (!buf)
+               return error("%s: unable to read file.", name);
+       if (type != OBJ_COMMIT)
+               return error("%s: cannot verify a non-commit object of type %s.",
+                               name, typename(type));
+
+       ret = run_gpg_verify(sha1, buf, size, verbose);
+
+       free(buf);
+       return ret;
+}
+
+static int git_verify_commit_config(const char *var, const char *value, void *cb)
+{
+       int status = git_gpg_config(var, value, cb);
+       if (status)
+               return status;
+       return git_default_config(var, value, cb);
+}
+
+int cmd_verify_commit(int argc, const char **argv, const char *prefix)
+{
+       int i = 1, verbose = 0, had_error = 0;
+       const struct option verify_commit_options[] = {
+               OPT__VERBOSE(&verbose, N_("print commit contents")),
+               OPT_END()
+       };
+
+       git_config(git_verify_commit_config, NULL);
+
+       argc = parse_options(argc, argv, prefix, verify_commit_options,
+                            verify_commit_usage, PARSE_OPT_KEEP_ARGV0);
+       if (argc <= i)
+               usage_with_options(verify_commit_usage, verify_commit_options);
+
+       /* sometimes the program was terminated because this signal
+        * was received in the process of writing the gpg input: */
+       signal(SIGPIPE, SIG_IGN);
+       while (i < argc)
+               if (verify_commit(argv[i++], verbose))
+                       had_error = 1;
+       return had_error;
+}
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 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 cbe1935ba6b17b6ccc37a400ea7cf0f67830e1d0..92fc9f1121776fbebf180b5e9d958982e8a98014 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -150,6 +150,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 +161,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 +184,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 +287,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 +331,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 +485,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 +507,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);
@@ -566,7 +585,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 *);
@@ -999,7 +1017,7 @@ extern int validate_headref(const char *ref);
 
 extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
-extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
+extern int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
 extern int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
 
 extern void *read_object_with_reference(const unsigned char *sha1,
@@ -1078,6 +1096,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,
@@ -1090,12 +1109,16 @@ struct checkout {
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
 
 struct cache_def {
-       char path[PATH_MAX + 1];
-       int len;
+       struct strbuf path;
        int flags;
        int track_flags;
        int prefix_len_stat_func;
 };
+#define CACHE_DEF_INIT { STRBUF_INIT, 0, 0, 0 }
+static inline void cache_def_clear(struct cache_def *cache)
+{
+       strbuf_release(&cache->path);
+}
 
 extern int has_symlink_leading_path(const char *name, int len);
 extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
index a1c4c3e8d845e8e791d7df0c1387e1b2262b5ecf..623eadcbb7dd2b7f58be0ef070b8c805a8101add 100755 (executable)
@@ -2,7 +2,7 @@
 bindir="$1"
 gitexecdir="$2"
 gitcmd="$3"
-if test "$bindir" != "$gitexecdir" -a -x "$gitcmd"
+if test "$bindir" != "$gitexecdir" && test -x "$gitcmd"
 then
        echo
        echo "!! You have installed git-* commands to new gitexecdir."
index cf36c3d71e3b8fc3382162ef939f3ed1f7d20775..a3ff0c9e60148e6dc98a6bd0d71d98cba4a7eae2 100644 (file)
@@ -132,6 +132,7 @@ git-update-server-info                  synchingrepositories
 git-upload-archive                      synchelpers
 git-upload-pack                         synchelpers
 git-var                                 plumbinginterrogators
+git-verify-commit                       ancillaryinterrogators
 git-verify-pack                         plumbinginterrogators
 git-verify-tag                          ancillaryinterrogators
 gitweb                                  ancillaryinterrogators
index cc114b53b096c6fc8574629b86659a639fdfed61..375c9c751adbf165c757832667102b761e1358c3 100644 (file)
@@ -117,4 +117,16 @@ static int stat_ ##slabname## realloc
  * catch because GCC silently parses it by default.
  */
 
+/*
+ * Statically initialize a commit slab named "var". Note that this
+ * evaluates "stride" multiple times! Example:
+ *
+ *   struct indegree indegrees = COMMIT_SLAB_INIT(1, indegrees);
+ *
+ */
+#define COMMIT_SLAB_INIT(stride, var) { \
+       COMMIT_SLAB_SIZE / sizeof(**((var).slab)) / (stride), \
+       (stride), 0, NULL \
+}
+
 #endif /* COMMIT_SLAB_H */
index dfc0eb02415edccba6195b4d2925196caa242a1a..acb74b55d4ee72ddf626754535a9febeb05ba945 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -17,7 +17,6 @@ static struct commit_extra_header *read_commit_extra_header_lines(const char *bu
 int save_commit_buffer = 1;
 
 const char *commit_type = "commit";
-static int commit_count;
 
 static struct commit *check_commit(struct object *obj,
                                   const unsigned char *sha1,
@@ -64,7 +63,6 @@ struct commit *lookup_commit(const unsigned char *sha1)
        struct object *obj = lookup_object(sha1);
        if (!obj) {
                struct commit *c = alloc_commit_node();
-               c->index = commit_count++;
                return create_object(sha1, OBJ_COMMIT, c);
        }
        if (!obj->type)
@@ -247,6 +245,76 @@ int unregister_shallow(const unsigned char *sha1)
        return 0;
 }
 
+struct commit_buffer {
+       void *buffer;
+       unsigned long size;
+};
+define_commit_slab(buffer_slab, struct commit_buffer);
+static struct buffer_slab buffer_slab = COMMIT_SLAB_INIT(1, buffer_slab);
+
+void set_commit_buffer(struct commit *commit, void *buffer, unsigned long size)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       v->buffer = buffer;
+       v->size = size;
+}
+
+const void *get_cached_commit_buffer(const struct commit *commit, unsigned long *sizep)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       if (sizep)
+               *sizep = v->size;
+       return v->buffer;
+}
+
+const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
+{
+       const void *ret = get_cached_commit_buffer(commit, sizep);
+       if (!ret) {
+               enum object_type type;
+               unsigned long size;
+               ret = read_sha1_file(commit->object.sha1, &type, &size);
+               if (!ret)
+                       die("cannot read commit object %s",
+                           sha1_to_hex(commit->object.sha1));
+               if (type != OBJ_COMMIT)
+                       die("expected commit for %s, got %s",
+                           sha1_to_hex(commit->object.sha1), typename(type));
+               if (sizep)
+                       *sizep = size;
+       }
+       return ret;
+}
+
+void unuse_commit_buffer(const struct commit *commit, const void *buffer)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       if (v->buffer != buffer)
+               free((void *)buffer);
+}
+
+void free_commit_buffer(struct commit *commit)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       free(v->buffer);
+       v->buffer = NULL;
+       v->size = 0;
+}
+
+const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       void *ret;
+
+       ret = v->buffer;
+       if (sizep)
+               *sizep = v->size;
+
+       v->buffer = NULL;
+       v->size = 0;
+       return ret;
+}
+
 int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
 {
        const char *tail = buffer;
@@ -324,7 +392,7 @@ int parse_commit(struct commit *item)
        }
        ret = parse_commit_buffer(item, buffer, size);
        if (save_commit_buffer && !ret) {
-               item->buffer = buffer;
+               set_commit_buffer(item, buffer, size);
                return 0;
        }
        free(buffer);
@@ -539,22 +607,12 @@ static void record_author_date(struct author_date_slab *author_date,
                               struct commit *commit)
 {
        const char *buf, *line_end, *ident_line;
-       char *buffer = NULL;
+       const char *buffer = get_commit_buffer(commit, NULL);
        struct ident_split ident;
        char *date_end;
        unsigned long date;
 
-       if (!commit->buffer) {
-               unsigned long size;
-               enum object_type type;
-               buffer = read_sha1_file(commit->object.sha1, &type, &size);
-               if (!buffer)
-                       return;
-       }
-
-       for (buf = commit->buffer ? commit->buffer : buffer;
-            buf;
-            buf = line_end + 1) {
+       for (buf = buffer; buf; buf = line_end + 1) {
                line_end = strchrnul(buf, '\n');
                if (!skip_prefix(buf, "author ", &ident_line)) {
                        if (!line_end[0] || line_end[1] == '\n')
@@ -574,7 +632,7 @@ static void record_author_date(struct author_date_slab *author_date,
        *(author_date_slab_at(author_date, commit)) = date;
 
 fail_exit:
-       free(buffer);
+       unuse_commit_buffer(commit, buffer);
 }
 
 static int compare_commits_by_author_date(const void *a_, const void *b_,
@@ -1079,17 +1137,14 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid)
        return 0;
 }
 
-int parse_signed_commit(const unsigned char *sha1,
+int parse_signed_commit(const struct commit *commit,
                        struct strbuf *payload, struct strbuf *signature)
 {
+
        unsigned long size;
-       enum object_type type;
-       char *buffer = read_sha1_file(sha1, &type, &size);
+       const char *buffer = get_commit_buffer(commit, &size);
        int in_signature, saw_signature = -1;
-       char *line, *tail;
-
-       if (!buffer || type != OBJ_COMMIT)
-               goto cleanup;
+       const char *line, *tail;
 
        line = buffer;
        tail = buffer + size;
@@ -1097,7 +1152,7 @@ int parse_signed_commit(const unsigned char *sha1,
        saw_signature = 0;
        while (line < tail) {
                const char *sig = NULL;
-               char *next = memchr(line, '\n', tail - line);
+               const char *next = memchr(line, '\n', tail - line);
 
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
@@ -1118,8 +1173,7 @@ int parse_signed_commit(const unsigned char *sha1,
                }
                line = next;
        }
- cleanup:
-       free(buffer);
+       unuse_commit_buffer(commit, buffer);
        return saw_signature;
 }
 
@@ -1209,14 +1263,14 @@ void check_commit_signature(const struct commit* commit, struct signature_check
 
        sigc->result = 'N';
 
-       if (parse_signed_commit(commit->object.sha1,
-                               &payload, &signature) <= 0)
+       if (parse_signed_commit(commit, &payload, &signature) <= 0)
                goto out;
        status = verify_signed_buffer(payload.buf, payload.len,
                                      signature.buf, signature.len,
                                      &gpg_output, &gpg_status);
        if (status && !gpg_output.len)
                goto out;
+       sigc->payload = strbuf_detach(&payload, NULL);
        sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
        sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
        parse_gpg_output(sigc);
@@ -1255,11 +1309,9 @@ struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
 {
        struct commit_extra_header *extra = NULL;
        unsigned long size;
-       enum object_type type;
-       char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
-       if (buffer && type == OBJ_COMMIT)
-               extra = read_commit_extra_header_lines(buffer, size, exclude);
-       free(buffer);
+       const char *buffer = get_commit_buffer(commit, &size);
+       extra = read_commit_extra_header_lines(buffer, size, exclude);
+       unuse_commit_buffer(commit, buffer);
        return extra;
 }
 
@@ -1342,7 +1394,8 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
        }
 }
 
-int commit_tree(const struct strbuf *msg, const unsigned char *tree,
+int commit_tree(const char *msg, size_t msg_len,
+               const unsigned char *tree,
                struct commit_list *parents, unsigned char *ret,
                const char *author, const char *sign_commit)
 {
@@ -1350,7 +1403,7 @@ int commit_tree(const struct strbuf *msg, const unsigned char *tree,
        int result;
 
        append_merge_tag_headers(parents, &tail);
-       result = commit_tree_extended(msg, tree, parents, ret,
+       result = commit_tree_extended(msg, msg_len, tree, parents, ret,
                                      author, sign_commit, extra);
        free_commit_extra_headers(extra);
        return result;
@@ -1471,7 +1524,8 @@ static const char commit_utf8_warn[] =
 "You may want to amend it after fixing the message, or set the config\n"
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
-int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
+int commit_tree_extended(const char *msg, size_t msg_len,
+                        const unsigned char *tree,
                         struct commit_list *parents, unsigned char *ret,
                         const char *author, const char *sign_commit,
                         struct commit_extra_header *extra)
@@ -1482,7 +1536,7 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
 
        assert_sha1_type(tree, OBJ_TREE);
 
-       if (memchr(msg->buf, '\0', msg->len))
+       if (memchr(msg, '\0', msg_len))
                return error("a NUL byte in commit log message not allowed.");
 
        /* Not having i18n.commitencoding is the same as having utf-8 */
@@ -1521,7 +1575,7 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
        strbuf_addch(&buffer, '\n');
 
        /* And add the comment */
-       strbuf_addbuf(&buffer, msg);
+       strbuf_add(&buffer, msg, msg_len);
 
        /* And check the encoding */
        if (encoding_is_utf8 && !verify_utf8(&buffer))
index a9f177ba488a7002409eb2cdee1f7e9c3673529f..2e1492a6e4d1c5fc5bea3a48180f128601c3461e 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -20,7 +20,6 @@ struct commit {
        unsigned long date;
        struct commit_list *parents;
        struct tree *tree;
-       char *buffer;
 };
 
 extern int save_commit_buffer;
@@ -51,6 +50,44 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
 int parse_commit(struct commit *item);
 void parse_commit_or_die(struct commit *item);
 
+/*
+ * Associate an object buffer with the commit. The ownership of the
+ * memory is handed over to the commit, and must be free()-able.
+ */
+void set_commit_buffer(struct commit *, void *buffer, unsigned long size);
+
+/*
+ * Get any cached object buffer associated with the commit. Returns NULL
+ * if none. The resulting memory should not be freed.
+ */
+const void *get_cached_commit_buffer(const struct commit *, unsigned long *size);
+
+/*
+ * Get the commit's object contents, either from cache or by reading the object
+ * from disk. The resulting memory should not be modified, and must be given
+ * to unuse_commit_buffer when the caller is done.
+ */
+const void *get_commit_buffer(const struct commit *, unsigned long *size);
+
+/*
+ * Tell the commit subsytem that we are done with a particular commit buffer.
+ * The commit and buffer should be the input and return value, respectively,
+ * from an earlier call to get_commit_buffer.  The buffer may or may not be
+ * freed by this call; callers should not access the memory afterwards.
+ */
+void unuse_commit_buffer(const struct commit *, const void *buffer);
+
+/*
+ * Free any cached object buffer associated with the commit.
+ */
+void free_commit_buffer(struct commit *);
+
+/*
+ * Disassociate any cached object buffer from the commit, but do not free it.
+ * The buffer (or NULL, if none) is returned.
+ */
+const void *detach_commit_buffer(struct commit *, unsigned long *sizep);
+
 /* Find beginning and length of commit subject. */
 int find_commit_subject(const char *commit_buffer, const char **subject);
 
@@ -115,10 +152,9 @@ struct userformat_want {
 
 extern int has_non_ascii(const char *text);
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
-extern char *logmsg_reencode(const struct commit *commit,
-                            char **commit_encoding,
-                            const char *output_encoding);
-extern void logmsg_free(char *msg, const struct commit *commit);
+extern const char *logmsg_reencode(const struct commit *commit,
+                                  char **commit_encoding,
+                                  const char *output_encoding);
 extern void get_commit_format(const char *arg, struct rev_info *);
 extern const char *format_subject(struct strbuf *sb, const char *msg,
                                  const char *line_separator);
@@ -261,11 +297,13 @@ struct commit_extra_header {
 extern void append_merge_tag_headers(struct commit_list *parents,
                                     struct commit_extra_header ***tail);
 
-extern int commit_tree(const struct strbuf *msg, const unsigned char *tree,
+extern int commit_tree(const char *msg, size_t msg_len,
+                      const unsigned char *tree,
                       struct commit_list *parents, unsigned char *ret,
                       const char *author, const char *sign_commit);
 
-extern int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
+extern int commit_tree_extended(const char *msg, size_t msg_len,
+                               const unsigned char *tree,
                                struct commit_list *parents, unsigned char *ret,
                                const char *author, const char *sign_commit,
                                struct commit_extra_header *);
@@ -287,7 +325,7 @@ struct merge_remote_desc {
  */
 struct commit *get_merge_parent(const char *name);
 
-extern int parse_signed_commit(const unsigned char *sha1,
+extern int parse_signed_commit(const struct commit *commit,
                               struct strbuf *message, struct strbuf *signature);
 extern void print_commit_list(struct commit_list *list,
                              const char *format_cur,
index a0e13bc862c052d8c18a6cba1c47a5a15fd12f7b..3baaa4dfaedd1e451f810a6bead28b2f7e9aa95d 100644 (file)
@@ -441,7 +441,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
 static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
 {
        int namelen;
-       static char alt_name[PATH_MAX];
+       char alt_name[PATH_MAX];
 
        if (!do_lstat(follow, file_name, buf))
                return 0;
@@ -831,9 +831,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                              const char *dir,
                              int prepend_cmd, int fhin, int fhout, int fherr)
 {
-       STARTUPINFO si;
+       STARTUPINFOW si;
        PROCESS_INFORMATION pi;
        struct strbuf envblk, args;
+       wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs;
        unsigned flags;
        BOOL ret;
 
@@ -865,9 +866,14 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
        memset(&si, 0, sizeof(si));
        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESTDHANDLES;
-       si.hStdInput = (HANDLE) _get_osfhandle(fhin);
-       si.hStdOutput = (HANDLE) _get_osfhandle(fhout);
-       si.hStdError = (HANDLE) _get_osfhandle(fherr);
+       si.hStdInput = winansi_get_osfhandle(fhin);
+       si.hStdOutput = winansi_get_osfhandle(fhout);
+       si.hStdError = winansi_get_osfhandle(fherr);
+
+       if (xutftowcs_path(wcmd, cmd) < 0)
+               return -1;
+       if (dir && xutftowcs_path(wdir, dir) < 0)
+               return -1;
 
        /* concatenate argv, quoting args as we go */
        strbuf_init(&args, 0);
@@ -886,6 +892,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                        free(quoted);
        }
 
+       wargs = xmalloc((2 * args.len + 1) * sizeof(wchar_t));
+       xutftowcs(wargs, args.buf, 2 * args.len + 1);
+       strbuf_release(&args);
+
        if (env) {
                int count = 0;
                char **e, **sorted_env;
@@ -907,12 +917,12 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
        }
 
        memset(&pi, 0, sizeof(pi));
-       ret = CreateProcess(cmd, args.buf, NULL, NULL, TRUE, flags,
-               env ? envblk.buf : NULL, dir, &si, &pi);
+       ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags,
+               env ? envblk.buf : NULL, dir ? wdir : NULL, &si, &pi);
 
        if (env)
                strbuf_release(&envblk);
-       strbuf_release(&args);
+       free(wargs);
 
        if (!ret) {
                errno = ENOENT;
@@ -941,10 +951,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
        return (pid_t)pi.dwProcessId;
 }
 
-static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
-                          int prepend_cmd)
+static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
 {
-       return mingw_spawnve_fd(cmd, argv, env, NULL, prepend_cmd, 0, 1, 2);
+       return mingw_spawnve_fd(cmd, argv, environ, NULL, prepend_cmd, 0, 1, 2);
 }
 
 pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
@@ -986,7 +995,7 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
        return pid;
 }
 
-static int try_shell_exec(const char *cmd, char *const *argv, char **env)
+static int try_shell_exec(const char *cmd, char *const *argv)
 {
        const char *interpr = parse_interpreter(cmd);
        char **path;
@@ -1004,7 +1013,7 @@ static int try_shell_exec(const char *cmd, char *const *argv, char **env)
                argv2 = xmalloc(sizeof(*argv) * (argc+1));
                argv2[0] = (char *)cmd; /* full path to the script file */
                memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
-               pid = mingw_spawnve(prog, argv2, env, 1);
+               pid = mingw_spawnv(prog, argv2, 1);
                if (pid >= 0) {
                        int status;
                        if (waitpid(pid, &status, 0) < 0)
@@ -1019,19 +1028,20 @@ static int try_shell_exec(const char *cmd, char *const *argv, char **env)
        return pid;
 }
 
-static void mingw_execve(const char *cmd, char *const *argv, char *const *env)
+int mingw_execv(const char *cmd, char *const *argv)
 {
        /* check if git_command is a shell script */
-       if (!try_shell_exec(cmd, argv, (char **)env)) {
+       if (!try_shell_exec(cmd, argv)) {
                int pid, status;
 
-               pid = mingw_spawnve(cmd, (const char **)argv, (char **)env, 0);
+               pid = mingw_spawnv(cmd, (const char **)argv, 0);
                if (pid < 0)
-                       return;
+                       return -1;
                if (waitpid(pid, &status, 0) < 0)
                        status = 255;
                exit(status);
        }
+       return -1;
 }
 
 int mingw_execvp(const char *cmd, char *const *argv)
@@ -1040,7 +1050,7 @@ int mingw_execvp(const char *cmd, char *const *argv)
        char *prog = path_lookup(cmd, path, 0);
 
        if (prog) {
-               mingw_execve(prog, argv, environ);
+               mingw_execv(prog, argv);
                free(prog);
        } else
                errno = ENOENT;
@@ -1049,12 +1059,6 @@ int mingw_execvp(const char *cmd, char *const *argv)
        return -1;
 }
 
-int mingw_execv(const char *cmd, char *const *argv)
-{
-       mingw_execve(cmd, argv, environ);
-       return -1;
-}
-
 int mingw_kill(pid_t pid, int sig)
 {
        if (pid > 0 && sig == SIGTERM) {
@@ -1847,3 +1851,150 @@ int mingw_offset_1st_component(const char *path)
 
        return offset + is_dir_sep(path[offset]);
 }
+
+int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
+{
+       int upos = 0, wpos = 0;
+       const unsigned char *utf = (const unsigned char*) utfs;
+       if (!utf || !wcs || wcslen < 1) {
+               errno = EINVAL;
+               return -1;
+       }
+       /* reserve space for \0 */
+       wcslen--;
+       if (utflen < 0)
+               utflen = INT_MAX;
+
+       while (upos < utflen) {
+               int c = utf[upos++] & 0xff;
+               if (utflen == INT_MAX && c == 0)
+                       break;
+
+               if (wpos >= wcslen) {
+                       wcs[wpos] = 0;
+                       errno = ERANGE;
+                       return -1;
+               }
+
+               if (c < 0x80) {
+                       /* ASCII */
+                       wcs[wpos++] = c;
+               } else if (c >= 0xc2 && c < 0xe0 && upos < utflen &&
+                               (utf[upos] & 0xc0) == 0x80) {
+                       /* 2-byte utf-8 */
+                       c = ((c & 0x1f) << 6);
+                       c |= (utf[upos++] & 0x3f);
+                       wcs[wpos++] = c;
+               } else if (c >= 0xe0 && c < 0xf0 && upos + 1 < utflen &&
+                               !(c == 0xe0 && utf[upos] < 0xa0) && /* over-long encoding */
+                               (utf[upos] & 0xc0) == 0x80 &&
+                               (utf[upos + 1] & 0xc0) == 0x80) {
+                       /* 3-byte utf-8 */
+                       c = ((c & 0x0f) << 12);
+                       c |= ((utf[upos++] & 0x3f) << 6);
+                       c |= (utf[upos++] & 0x3f);
+                       wcs[wpos++] = c;
+               } else if (c >= 0xf0 && c < 0xf5 && upos + 2 < utflen &&
+                               wpos + 1 < wcslen &&
+                               !(c == 0xf0 && utf[upos] < 0x90) && /* over-long encoding */
+                               !(c == 0xf4 && utf[upos] >= 0x90) && /* > \u10ffff */
+                               (utf[upos] & 0xc0) == 0x80 &&
+                               (utf[upos + 1] & 0xc0) == 0x80 &&
+                               (utf[upos + 2] & 0xc0) == 0x80) {
+                       /* 4-byte utf-8: convert to \ud8xx \udcxx surrogate pair */
+                       c = ((c & 0x07) << 18);
+                       c |= ((utf[upos++] & 0x3f) << 12);
+                       c |= ((utf[upos++] & 0x3f) << 6);
+                       c |= (utf[upos++] & 0x3f);
+                       c -= 0x10000;
+                       wcs[wpos++] = 0xd800 | (c >> 10);
+                       wcs[wpos++] = 0xdc00 | (c & 0x3ff);
+               } else if (c >= 0xa0) {
+                       /* invalid utf-8 byte, printable unicode char: convert 1:1 */
+                       wcs[wpos++] = c;
+               } else {
+                       /* invalid utf-8 byte, non-printable unicode: convert to hex */
+                       static const char *hex = "0123456789abcdef";
+                       wcs[wpos++] = hex[c >> 4];
+                       if (wpos < wcslen)
+                               wcs[wpos++] = hex[c & 0x0f];
+               }
+       }
+       wcs[wpos] = 0;
+       return wpos;
+}
+
+int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
+{
+       if (!wcs || !utf || utflen < 1) {
+               errno = EINVAL;
+               return -1;
+       }
+       utflen = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf, utflen, NULL, NULL);
+       if (utflen)
+               return utflen - 1;
+       errno = ERANGE;
+       return -1;
+}
+
+/*
+ * Disable MSVCRT command line wildcard expansion (__getmainargs called from
+ * mingw startup code, see init.c in mingw runtime).
+ */
+int _CRT_glob = 0;
+
+typedef struct {
+       int newmode;
+} _startupinfo;
+
+extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
+               _startupinfo *si);
+
+static NORETURN void die_startup()
+{
+       fputs("fatal: not enough memory for initialization", stderr);
+       exit(128);
+}
+
+void mingw_startup()
+{
+       int i, len, maxlen, argc;
+       char *buffer;
+       wchar_t **wenv, **wargv;
+       _startupinfo si;
+
+       /* get wide char arguments and environment */
+       si.newmode = 0;
+       if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
+               die_startup();
+
+       /* determine size of argv and environ conversion buffer */
+       maxlen = wcslen(_wpgmptr);
+       for (i = 1; i < argc; i++)
+               maxlen = max(maxlen, wcslen(wargv[i]));
+
+       /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
+       maxlen = 3 * maxlen + 1;
+       buffer = xmalloc(maxlen);
+
+       /* convert command line arguments and environment to UTF-8 */
+       len = xwcstoutf(buffer, _wpgmptr, maxlen);
+       __argv[0] = xmemdupz(buffer, len);
+       for (i = 1; i < argc; i++) {
+               len = xwcstoutf(buffer, wargv[i], maxlen);
+               __argv[i] = xmemdupz(buffer, len);
+       }
+       free(buffer);
+
+       /* initialize critical section for waitpid pinfo_t list */
+       InitializeCriticalSection(&pinfo_cs);
+
+       /* set up default file mode and file modes for stdin/out/err */
+       _fmode = _O_BINARY;
+       _setmode(_fileno(stdin), _O_BINARY);
+       _setmode(_fileno(stdout), _O_BINARY);
+       _setmode(_fileno(stderr), _O_BINARY);
+
+       /* initialize Unicode console */
+       winansi_init();
+}
index 3eaf822e28e23e6e46e82a9bb7c9db1ed42d252c..7ff2376335dea7a092c626f74f52413be1c81e74 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
@@ -317,12 +320,8 @@ int mingw_raise(int sig);
  * ANSI emulation wrappers
  */
 
-int winansi_fputs(const char *str, FILE *stream);
-int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2)));
-int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3)));
-#define fputs winansi_fputs
-#define printf(...) winansi_printf(__VA_ARGS__)
-#define fprintf(...) winansi_fprintf(__VA_ARGS__)
+void winansi_init(void);
+HANDLE winansi_get_osfhandle(int fd);
 
 /*
  * git specific compatibility
@@ -355,6 +354,110 @@ void mingw_open_html(const char *path);
 char **make_augmented_environ(const char *const *vars);
 void free_environ(char **env);
 
+/**
+ * Converts UTF-8 encoded string to UTF-16LE.
+ *
+ * To support repositories with legacy-encoded file names, invalid UTF-8 bytes
+ * 0xa0 - 0xff are converted to corresponding printable Unicode chars \u00a0 -
+ * \u00ff, and invalid UTF-8 bytes 0x80 - 0x9f (which would make non-printable
+ * Unicode) are converted to hex-code.
+ *
+ * Lead-bytes not followed by an appropriate number of trail-bytes, over-long
+ * encodings and 4-byte encodings > \u10ffff are detected as invalid UTF-8.
+ *
+ * Maximum space requirement for the target buffer is two wide chars per UTF-8
+ * char (((strlen(utf) * 2) + 1) [* sizeof(wchar_t)]).
+ *
+ * The maximum space is needed only if the entire input string consists of
+ * invalid UTF-8 bytes in range 0x80-0x9f, as per the following table:
+ *
+ *               |                   | UTF-8 | UTF-16 |
+ *   Code point  |  UTF-8 sequence   | bytes | words  | ratio
+ * --------------+-------------------+-------+--------+-------
+ * 000000-00007f | 0-7f              |   1   |   1    |  1
+ * 000080-0007ff | c2-df + 80-bf     |   2   |   1    |  0.5
+ * 000800-00ffff | e0-ef + 2 * 80-bf |   3   |   1    |  0.33
+ * 010000-10ffff | f0-f4 + 3 * 80-bf |   4   |  2 (a) |  0.5
+ * invalid       | 80-9f             |   1   |  2 (b) |  2
+ * invalid       | a0-ff             |   1   |   1    |  1
+ *
+ * (a) encoded as UTF-16 surrogate pair
+ * (b) encoded as two hex digits
+ *
+ * Note that, while the UTF-8 encoding scheme can be extended to 5-byte, 6-byte
+ * or even indefinite-byte sequences, the largest valid code point \u10ffff
+ * encodes as only 4 UTF-8 bytes.
+ *
+ * Parameters:
+ * wcs: wide char target buffer
+ * utf: string to convert
+ * wcslen: size of target buffer (in wchar_t's)
+ * utflen: size of string to convert, or -1 if 0-terminated
+ *
+ * Returns:
+ * length of converted string (_wcslen(wcs)), or -1 on failure
+ *
+ * Errors:
+ * EINVAL: one of the input parameters is invalid (e.g. NULL)
+ * ERANGE: the output buffer is too small
+ */
+int xutftowcsn(wchar_t *wcs, const char *utf, size_t wcslen, int utflen);
+
+/**
+ * Simplified variant of xutftowcsn, assumes input string is \0-terminated.
+ */
+static inline int xutftowcs(wchar_t *wcs, const char *utf, size_t wcslen)
+{
+       return xutftowcsn(wcs, utf, wcslen, -1);
+}
+
+/**
+ * Simplified file system specific variant of xutftowcsn, assumes output
+ * buffer size is MAX_PATH wide chars and input string is \0-terminated,
+ * fails with ENAMETOOLONG if input string is too long.
+ */
+static inline int xutftowcs_path(wchar_t *wcs, const char *utf)
+{
+       int result = xutftowcsn(wcs, utf, MAX_PATH, -1);
+       if (result < 0 && errno == ERANGE)
+               errno = ENAMETOOLONG;
+       return result;
+}
+
+/**
+ * Converts UTF-16LE encoded string to UTF-8.
+ *
+ * Maximum space requirement for the target buffer is three UTF-8 chars per
+ * wide char ((_wcslen(wcs) * 3) + 1).
+ *
+ * The maximum space is needed only if the entire input string consists of
+ * UTF-16 words in range 0x0800-0xd7ff or 0xe000-0xffff (i.e. \u0800-\uffff
+ * modulo surrogate pairs), as per the following table:
+ *
+ *               |                       | UTF-16 | UTF-8 |
+ *   Code point  |  UTF-16 sequence      | words  | bytes | ratio
+ * --------------+-----------------------+--------+-------+-------
+ * 000000-00007f | 0000-007f             |   1    |   1   |  1
+ * 000080-0007ff | 0080-07ff             |   1    |   2   |  2
+ * 000800-00ffff | 0800-d7ff / e000-ffff |   1    |   3   |  3
+ * 010000-10ffff | d800-dbff + dc00-dfff |   2    |   4   |  2
+ *
+ * Note that invalid code points > 10ffff cannot be represented in UTF-16.
+ *
+ * Parameters:
+ * utf: target buffer
+ * wcs: wide string to convert
+ * utflen: size of target buffer
+ *
+ * Returns:
+ * length of converted string, or -1 on failure
+ *
+ * Errors:
+ * EINVAL: one of the input parameters is invalid (e.g. NULL)
+ * ERANGE: the output buffer is too small
+ */
+int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen);
+
 /*
  * A critical section used in the implementation of the spawn
  * functions (mingw_spawnv[p]e()) and waitpid(). Intialised in
@@ -363,22 +466,16 @@ void free_environ(char **env);
 extern CRITICAL_SECTION pinfo_cs;
 
 /*
- * A replacement of main() that ensures that argv[0] has a path
- * and that default fmode and std(in|out|err) are in binary mode
+ * A replacement of main() that adds win32 specific initialization.
  */
 
+void mingw_startup();
 #define main(c,v) dummy_decl_mingw_main(); \
 static int mingw_main(c,v); \
 int main(int argc, char **argv) \
 { \
-       extern CRITICAL_SECTION pinfo_cs; \
-       _fmode = _O_BINARY; \
-       _setmode(_fileno(stdin), _O_BINARY); \
-       _setmode(_fileno(stdout), _O_BINARY); \
-       _setmode(_fileno(stderr), _O_BINARY); \
-       argv[0] = xstrdup(_pgmptr); \
-       InitializeCriticalSection(&pinfo_cs); \
-       return mingw_main(argc, argv); \
+       mingw_startup(); \
+       return mingw_main(__argc, (void *)__argv); \
 } \
 static int mingw_main(c,v)
 
index 7a0debe51bcad9f8d46c9c675aa3a6bbaaf7f537..82a515c21b322a0e50c7b5dbdd12e74341b20e98 100644 (file)
@@ -1,96 +1,91 @@
-#include "../git-compat-util.h"
-#include "dirent.h"
+#include "../../git-compat-util.h"
 
 struct DIR {
        struct dirent dd_dir; /* includes d_type */
        HANDLE dd_handle;     /* FindFirstFile handle */
        int dd_stat;          /* 0-based index */
-       char dd_name[1];      /* extend struct */
 };
 
+static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata)
+{
+       /* copy file name from WIN32_FIND_DATA to dirent */
+       memcpy(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
+
+       /* Set file type, based on WIN32_FIND_DATA */
+       if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+               ent->d_type = DT_DIR;
+       else
+               ent->d_type = DT_REG;
+}
+
 DIR *opendir(const char *name)
 {
-       DWORD attrs = GetFileAttributesA(name);
+       char pattern[MAX_PATH];
+       WIN32_FIND_DATAA fdata;
+       HANDLE h;
        int len;
-       DIR *p;
+       DIR *dir;
 
-       /* check for valid path */
-       if (attrs == INVALID_FILE_ATTRIBUTES) {
-               errno = ENOENT;
+       /* check that name is not NULL */
+       if (!name) {
+               errno = EINVAL;
                return NULL;
        }
-
-       /* check if it's a directory */
-       if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
-               errno = ENOTDIR;
-               return NULL;
-       }
-
        /* check that the pattern won't be too long for FindFirstFileA */
        len = strlen(name);
-       if (is_dir_sep(name[len - 1]))
-               len--;
        if (len + 2 >= MAX_PATH) {
                errno = ENAMETOOLONG;
                return NULL;
        }
-
-       p = malloc(sizeof(DIR) + len + 2);
-       if (!p)
+       /* copy name to temp buffer */
+       memcpy(pattern, name, len + 1);
+
+       /* append optional '/' and wildcard '*' */
+       if (len && !is_dir_sep(pattern[len - 1]))
+               pattern[len++] = '/';
+       pattern[len++] = '*';
+       pattern[len] = 0;
+
+       /* open find handle */
+       h = FindFirstFileA(pattern, &fdata);
+       if (h == INVALID_HANDLE_VALUE) {
+               DWORD err = GetLastError();
+               errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
                return NULL;
+       }
 
-       memset(p, 0, sizeof(DIR) + len + 2);
-       strcpy(p->dd_name, name);
-       p->dd_name[len] = '/';
-       p->dd_name[len+1] = '*';
-
-       p->dd_handle = INVALID_HANDLE_VALUE;
-       return p;
+       /* initialize DIR structure and copy first dir entry */
+       dir = xmalloc(sizeof(DIR));
+       dir->dd_handle = h;
+       dir->dd_stat = 0;
+       finddata2dirent(&dir->dd_dir, &fdata);
+       return dir;
 }
 
 struct dirent *readdir(DIR *dir)
 {
-       WIN32_FIND_DATAA buf;
-       HANDLE handle;
-
-       if (!dir || !dir->dd_handle) {
+       if (!dir) {
                errno = EBADF; /* No set_errno for mingw */
                return NULL;
        }
 
-       if (dir->dd_handle == INVALID_HANDLE_VALUE && dir->dd_stat == 0) {
-               DWORD lasterr;
-               handle = FindFirstFileA(dir->dd_name, &buf);
-               lasterr = GetLastError();
-               dir->dd_handle = handle;
-               if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
-                       errno = err_win_to_posix(lasterr);
+       /* if first entry, dirent has already been set up by opendir */
+       if (dir->dd_stat) {
+               /* get next entry and convert from WIN32_FIND_DATA to dirent */
+               WIN32_FIND_DATAA fdata;
+               if (FindNextFileA(dir->dd_handle, &fdata)) {
+                       finddata2dirent(&dir->dd_dir, &fdata);
+               } else {
+                       DWORD lasterr = GetLastError();
+                       /* POSIX says you shouldn't set errno when readdir can't
+                          find any more files; so, if another error we leave it set. */
+                       if (lasterr != ERROR_NO_MORE_FILES)
+                               errno = err_win_to_posix(lasterr);
                        return NULL;
                }
-       } else if (dir->dd_handle == INVALID_HANDLE_VALUE) {
-               return NULL;
-       } else if (!FindNextFileA(dir->dd_handle, &buf)) {
-               DWORD lasterr = GetLastError();
-               FindClose(dir->dd_handle);
-               dir->dd_handle = INVALID_HANDLE_VALUE;
-               /* POSIX says you shouldn't set errno when readdir can't
-                  find any more files; so, if another error we leave it set. */
-               if (lasterr != ERROR_NO_MORE_FILES)
-                       errno = err_win_to_posix(lasterr);
-               return NULL;
        }
 
-       /* We get here if `buf' contains valid data.  */
-       strcpy(dir->dd_dir.d_name, buf.cFileName);
        ++dir->dd_stat;
-
-       /* Set file type, based on WIN32_FIND_DATA */
-       dir->dd_dir.d_type = 0;
-       if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-               dir->dd_dir.d_type |= DT_DIR;
-       else
-               dir->dd_dir.d_type |= DT_REG;
-
        return &dir->dd_dir;
 }
 
@@ -101,8 +96,7 @@ int closedir(DIR *dir)
                return -1;
        }
 
-       if (dir->dd_handle != INVALID_HANDLE_VALUE)
-               FindClose(dir->dd_handle);
+       FindClose(dir->dd_handle);
        free(dir);
        return 0;
 }
index 927a25ca765ef4f393ba28d54e2478b29fca878e..8838cd61fc282b3713db76e088130f82b6c6fe33 100644 (file)
@@ -9,12 +9,8 @@ typedef struct DIR DIR;
 #define DT_LNK     3
 
 struct dirent {
-       long d_ino;                      /* Always zero. */
-       char d_name[FILENAME_MAX];       /* File name. */
-       union {
-               unsigned short d_reclen; /* Always zero. */
-               unsigned char  d_type;   /* Reimplementation adds this */
-       };
+       unsigned char d_type;      /* file type to prevent lstat after readdir */
+       char d_name[MAX_PATH];     /* file name */
 };
 
 DIR *opendir(const char *dirname);
index dedce2104eaf5cefbb1abef1b7921eb99c67a75e..efc5bb3a4b63166eccb33f6ec6a8ad57e4c9ac36 100644 (file)
@@ -2,15 +2,10 @@
  * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
  */
 
+#undef NOGDI
 #include "../git-compat-util.h"
-
-/*
- Functions to be wrapped:
-*/
-#undef printf
-#undef fprintf
-#undef fputs
-/* TODO: write */
+#include <wingdi.h>
+#include <winreg.h>
 
 /*
  ANSI codes used by git: m, K
@@ -23,29 +18,114 @@ static HANDLE console;
 static WORD plain_attr;
 static WORD attr;
 static int negative;
+static int non_ascii_used = 0;
+static HANDLE hthread, hread, hwrite;
+static HANDLE hconsole1, hconsole2;
+
+#ifdef __MINGW32__
+typedef struct _CONSOLE_FONT_INFOEX {
+       ULONG cbSize;
+       DWORD nFont;
+       COORD dwFontSize;
+       UINT FontFamily;
+       UINT FontWeight;
+       WCHAR FaceName[LF_FACESIZE];
+} CONSOLE_FONT_INFOEX, *PCONSOLE_FONT_INFOEX;
+#endif
+
+typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL,
+               PCONSOLE_FONT_INFOEX);
+
+static void warn_if_raster_font(void)
+{
+       DWORD fontFamily = 0;
+       PGETCURRENTCONSOLEFONTEX pGetCurrentConsoleFontEx;
+
+       /* don't bother if output was ascii only */
+       if (!non_ascii_used)
+               return;
+
+       /* GetCurrentConsoleFontEx is available since Vista */
+       pGetCurrentConsoleFontEx = (PGETCURRENTCONSOLEFONTEX) GetProcAddress(
+                       GetModuleHandle("kernel32.dll"),
+                       "GetCurrentConsoleFontEx");
+       if (pGetCurrentConsoleFontEx) {
+               CONSOLE_FONT_INFOEX cfi;
+               cfi.cbSize = sizeof(cfi);
+               if (pGetCurrentConsoleFontEx(console, 0, &cfi))
+                       fontFamily = cfi.FontFamily;
+       } else {
+               /* pre-Vista: check default console font in registry */
+               HKEY hkey;
+               if (ERROR_SUCCESS == RegOpenKeyExA(HKEY_CURRENT_USER, "Console",
+                               0, KEY_READ, &hkey)) {
+                       DWORD size = sizeof(fontFamily);
+                       RegQueryValueExA(hkey, "FontFamily", NULL, NULL,
+                                       (LPVOID) &fontFamily, &size);
+                       RegCloseKey(hkey);
+               }
+       }
+
+       if (!(fontFamily & TMPF_TRUETYPE)) {
+               const wchar_t *msg = L"\nWarning: Your console font probably "
+                       L"doesn\'t support Unicode. If you experience strange "
+                       L"characters in the output, consider switching to a "
+                       L"TrueType font such as Consolas!\n";
+               DWORD dummy;
+               WriteConsoleW(console, msg, wcslen(msg), &dummy, NULL);
+       }
+}
 
-static void init(void)
+static int is_console(int fd)
 {
        CONSOLE_SCREEN_BUFFER_INFO sbi;
+       HANDLE hcon;
 
        static int initialized = 0;
-       if (initialized)
-               return;
 
-       console = GetStdHandle(STD_OUTPUT_HANDLE);
-       if (console == INVALID_HANDLE_VALUE)
-               console = NULL;
+       /* get OS handle of the file descriptor */
+       hcon = (HANDLE) _get_osfhandle(fd);
+       if (hcon == INVALID_HANDLE_VALUE)
+               return 0;
 
-       if (!console)
-               return;
+       /* check if its a device (i.e. console, printer, serial port) */
+       if (GetFileType(hcon) != FILE_TYPE_CHAR)
+               return 0;
 
-       GetConsoleScreenBufferInfo(console, &sbi);
-       attr = plain_attr = sbi.wAttributes;
-       negative = 0;
+       /* check if its a handle to a console output screen buffer */
+       if (!GetConsoleScreenBufferInfo(hcon, &sbi))
+               return 0;
+
+       /* initialize attributes */
+       if (!initialized) {
+               console = hcon;
+               attr = plain_attr = sbi.wAttributes;
+               negative = 0;
+               initialized = 1;
+       }
 
-       initialized = 1;
+       return 1;
 }
 
+#define BUFFER_SIZE 4096
+#define MAX_PARAMS 16
+
+static void write_console(unsigned char *str, size_t len)
+{
+       /* only called from console_thread, so a static buffer will do */
+       static wchar_t wbuf[2 * BUFFER_SIZE + 1];
+       DWORD dummy;
+
+       /* convert utf-8 to utf-16 */
+       int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
+
+       /* write directly to console */
+       WriteConsoleW(console, wbuf, wlen, &dummy, NULL);
+
+       /* remember if non-ascii characters are printed */
+       if (wlen != len)
+               non_ascii_used = 1;
+}
 
 #define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
 #define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
@@ -90,18 +170,13 @@ static void erase_in_line(void)
                &dummy);
 }
 
-
-static const char *set_attr(const char *str)
+static void set_attr(char func, const int *params, int paramlen)
 {
-       const char *func;
-       size_t len = strspn(str, "0123456789;");
-       func = str + len;
-
-       switch (*func) {
+       int i;
+       switch (func) {
        case 'm':
-               do {
-                       long val = strtol(str, (char **)&str, 10);
-                       switch (val) {
+               for (i = 0; i < paramlen; i++) {
+                       switch (params[i]) {
                        case 0: /* reset */
                                attr = plain_attr;
                                negative = 0;
@@ -224,9 +299,7 @@ static const char *set_attr(const char *str)
                                /* Unsupported code */
                                break;
                        }
-                       str++;
-               } while (*(str-1) == ';');
-
+               }
                set_console_attr();
                break;
        case 'K':
@@ -236,122 +309,271 @@ static const char *set_attr(const char *str)
                /* Unsupported code */
                break;
        }
-
-       return func + 1;
 }
 
-static int ansi_emulate(const char *str, FILE *stream)
+enum {
+       TEXT = 0, ESCAPE = 033, BRACKET = '['
+};
+
+static DWORD WINAPI console_thread(LPVOID unused)
 {
-       int rv = 0;
-       const char *pos = str;
-
-       while (*pos) {
-               pos = strstr(str, "\033[");
-               if (pos) {
-                       size_t len = pos - str;
-
-                       if (len) {
-                               size_t out_len = fwrite(str, 1, len, stream);
-                               rv += out_len;
-                               if (out_len < len)
-                                       return rv;
+       unsigned char buffer[BUFFER_SIZE];
+       DWORD bytes;
+       int start, end = 0, c, parampos = 0, state = TEXT;
+       int params[MAX_PARAMS];
+
+       while (1) {
+               /* read next chunk of bytes from the pipe */
+               if (!ReadFile(hread, buffer + end, BUFFER_SIZE - end, &bytes,
+                               NULL)) {
+                       /* exit if pipe has been closed or disconnected */
+                       if (GetLastError() == ERROR_PIPE_NOT_CONNECTED ||
+                                       GetLastError() == ERROR_BROKEN_PIPE)
+                               break;
+                       /* ignore other errors */
+                       continue;
+               }
+
+               /* scan the bytes and handle ANSI control codes */
+               bytes += end;
+               start = end = 0;
+               while (end < bytes) {
+                       c = buffer[end++];
+                       switch (state) {
+                       case TEXT:
+                               if (c == ESCAPE) {
+                                       /* print text seen so far */
+                                       if (end - 1 > start)
+                                               write_console(buffer + start,
+                                                       end - 1 - start);
+
+                                       /* then start parsing escape sequence */
+                                       start = end - 1;
+                                       memset(params, 0, sizeof(params));
+                                       parampos = 0;
+                                       state = ESCAPE;
+                               }
+                               break;
+
+                       case ESCAPE:
+                               /* continue if "\033[", otherwise bail out */
+                               state = (c == BRACKET) ? BRACKET : TEXT;
+                               break;
+
+                       case BRACKET:
+                               /* parse [0-9;]* into array of parameters */
+                               if (c >= '0' && c <= '9') {
+                                       params[parampos] *= 10;
+                                       params[parampos] += c - '0';
+                               } else if (c == ';') {
+                                       /*
+                                        * next parameter, bail out if out of
+                                        * bounds
+                                        */
+                                       parampos++;
+                                       if (parampos >= MAX_PARAMS)
+                                               state = TEXT;
+                               } else {
+                                       /*
+                                        * end of escape sequence, change
+                                        * console attributes
+                                        */
+                                       set_attr(c, params, parampos + 1);
+                                       start = end;
+                                       state = TEXT;
+                               }
+                               break;
                        }
+               }
 
-                       str = pos + 2;
-                       rv += 2;
+               /* print remaining text unless parsing an escape sequence */
+               if (state == TEXT && end > start) {
+                       /* check for incomplete UTF-8 sequences and fix end */
+                       if (buffer[end - 1] >= 0x80) {
+                               if (buffer[end -1] >= 0xc0)
+                                       end--;
+                               else if (end - 1 > start &&
+                                               buffer[end - 2] >= 0xe0)
+                                       end -= 2;
+                               else if (end - 2 > start &&
+                                               buffer[end - 3] >= 0xf0)
+                                       end -= 3;
+                       }
 
-                       fflush(stream);
+                       /* print remaining complete UTF-8 sequences */
+                       if (end > start)
+                               write_console(buffer + start, end - start);
 
-                       pos = set_attr(str);
-                       rv += pos - str;
-                       str = pos;
+                       /* move remaining bytes to the front */
+                       if (end < bytes)
+                               memmove(buffer, buffer + end, bytes - end);
+                       end = bytes - end;
                } else {
-                       rv += strlen(str);
-                       fputs(str, stream);
-                       return rv;
+                       /* all data has been consumed, mark buffer empty */
+                       end = 0;
                }
        }
-       return rv;
+
+       /* check if the console font supports unicode */
+       warn_if_raster_font();
+
+       CloseHandle(hread);
+       return 0;
 }
 
-int winansi_fputs(const char *str, FILE *stream)
+static void winansi_exit(void)
 {
-       int rv;
-
-       if (!isatty(fileno(stream)))
-               return fputs(str, stream);
+       /* flush all streams */
+       _flushall();
 
-       init();
+       /* signal console thread to exit */
+       FlushFileBuffers(hwrite);
+       DisconnectNamedPipe(hwrite);
 
-       if (!console)
-               return fputs(str, stream);
+       /* wait for console thread to copy remaining data */
+       WaitForSingleObject(hthread, INFINITE);
 
-       rv = ansi_emulate(str, stream);
+       /* cleanup handles... */
+       CloseHandle(hwrite);
+       CloseHandle(hthread);
+}
 
-       if (rv >= 0)
-               return 0;
-       else
-               return EOF;
+static void die_lasterr(const char *fmt, ...)
+{
+       va_list params;
+       va_start(params, fmt);
+       errno = err_win_to_posix(GetLastError());
+       die_errno(fmt, params);
+       va_end(params);
 }
 
-static int winansi_vfprintf(FILE *stream, const char *format, va_list list)
+static HANDLE duplicate_handle(HANDLE hnd)
 {
-       int len, rv;
-       char small_buf[256];
-       char *buf = small_buf;
-       va_list cp;
+       HANDLE hresult, hproc = GetCurrentProcess();
+       if (!DuplicateHandle(hproc, hnd, hproc, &hresult, 0, TRUE,
+                       DUPLICATE_SAME_ACCESS))
+               die_lasterr("DuplicateHandle(%li) failed", (long) hnd);
+       return hresult;
+}
 
-       if (!isatty(fileno(stream)))
-               goto abort;
 
-       init();
+/*
+ * Make MSVCRT's internal file descriptor control structure accessible
+ * so that we can tweak OS handles and flags directly (we need MSVCRT
+ * to treat our pipe handle as if it were a console).
+ *
+ * We assume that the ioinfo structure (exposed by MSVCRT.dll via
+ * __pioinfo) starts with the OS handle and the flags. The exact size
+ * varies between MSVCRT versions, so we try different sizes until
+ * toggling the FDEV bit of _pioinfo(1)->osflags is reflected in
+ * isatty(1).
+ */
+typedef struct {
+       HANDLE osfhnd;
+       char osflags;
+} ioinfo;
 
-       if (!console)
-               goto abort;
+extern __declspec(dllimport) ioinfo *__pioinfo[];
 
-       va_copy(cp, list);
-       len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
-       va_end(cp);
+static size_t sizeof_ioinfo = 0;
 
-       if (len > sizeof(small_buf) - 1) {
-               buf = malloc(len + 1);
-               if (!buf)
-                       goto abort;
+#define IOINFO_L2E 5
+#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
 
-               len = vsnprintf(buf, len + 1, format, list);
-       }
+#define FDEV  0x40
 
-       rv = ansi_emulate(buf, stream);
+static inline ioinfo* _pioinfo(int fd)
+{
+       return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
+                       (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
+}
 
-       if (buf != small_buf)
-               free(buf);
-       return rv;
+static int init_sizeof_ioinfo()
+{
+       int istty, wastty;
+       /* don't init twice */
+       if (sizeof_ioinfo)
+               return sizeof_ioinfo >= 256;
+
+       sizeof_ioinfo = sizeof(ioinfo);
+       wastty = isatty(1);
+       while (sizeof_ioinfo < 256) {
+               /* toggle FDEV flag, check isatty, then toggle back */
+               _pioinfo(1)->osflags ^= FDEV;
+               istty = isatty(1);
+               _pioinfo(1)->osflags ^= FDEV;
+               /* return if we found the correct size */
+               if (istty != wastty)
+                       return 0;
+               sizeof_ioinfo += sizeof(void*);
+       }
+       error("Tweaking file descriptors doesn't work with this MSVCRT.dll");
+       return 1;
+}
 
-abort:
-       rv = vfprintf(stream, format, list);
-       return rv;
+static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
+{
+       ioinfo *pioinfo;
+       HANDLE old_handle;
+
+       /* init ioinfo size if we haven't done so */
+       if (init_sizeof_ioinfo())
+               return INVALID_HANDLE_VALUE;
+
+       /* get ioinfo pointer and change the handles */
+       pioinfo = _pioinfo(fd);
+       old_handle = pioinfo->osfhnd;
+       pioinfo->osfhnd = new_handle;
+       return old_handle;
 }
 
-int winansi_fprintf(FILE *stream, const char *format, ...)
+void winansi_init(void)
 {
-       va_list list;
-       int rv;
+       int con1, con2;
+       char name[32];
 
-       va_start(list, format);
-       rv = winansi_vfprintf(stream, format, list);
-       va_end(list);
+       /* check if either stdout or stderr is a console output screen buffer */
+       con1 = is_console(1);
+       con2 = is_console(2);
+       if (!con1 && !con2)
+               return;
 
-       return rv;
+       /* create a named pipe to communicate with the console thread */
+       sprintf(name, "\\\\.\\pipe\\winansi%lu", GetCurrentProcessId());
+       hwrite = CreateNamedPipe(name, PIPE_ACCESS_OUTBOUND,
+               PIPE_TYPE_BYTE | PIPE_WAIT, 1, BUFFER_SIZE, 0, 0, NULL);
+       if (hwrite == INVALID_HANDLE_VALUE)
+               die_lasterr("CreateNamedPipe failed");
+
+       hread = CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+       if (hread == INVALID_HANDLE_VALUE)
+               die_lasterr("CreateFile for named pipe failed");
+
+       /* start console spool thread on the pipe's read end */
+       hthread = CreateThread(NULL, 0, console_thread, NULL, 0, NULL);
+       if (hthread == INVALID_HANDLE_VALUE)
+               die_lasterr("CreateThread(console_thread) failed");
+
+       /* schedule cleanup routine */
+       if (atexit(winansi_exit))
+               die_errno("atexit(winansi_exit) failed");
+
+       /* redirect stdout / stderr to the pipe */
+       if (con1)
+               hconsole1 = swap_osfhnd(1, duplicate_handle(hwrite));
+       if (con2)
+               hconsole2 = swap_osfhnd(2, duplicate_handle(hwrite));
 }
 
-int winansi_printf(const char *format, ...)
+/*
+ * Returns the real console handle if stdout / stderr is a pipe redirecting
+ * to the console. Allows spawn / exec to pass the console to the next process.
+ */
+HANDLE winansi_get_osfhandle(int fd)
 {
-       va_list list;
-       int rv;
-
-       va_start(list, format);
-       rv = winansi_vfprintf(stdout, format, list);
-       va_end(list);
-
-       return rv;
+       HANDLE hnd = (HANDLE) _get_osfhandle(fd);
+       if ((fd == 1 || fd == 2) && isatty(fd)
+           && GetFileType(hnd) == FILE_TYPE_PIPE)
+               return (fd == 1) ? hconsole1 : hconsole2;
+       return hnd;
 }
index 1ae675b05344912d8798b9856b60b03c282cf908..8131c81985d97c23d19c7ef489f0db988f6fbbc7 100644 (file)
@@ -354,6 +354,7 @@ ifeq ($(uname_S),Windows)
        NO_POSIX_GOODIES = UnfortunatelyYes
        NATIVE_CRLF = YesPlease
        DEFAULT_HELP_FORMAT = html
+       NO_D_INO_IN_DIRENT = YesPlease
 
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
@@ -503,6 +504,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_INET_NTOP = YesPlease
        NO_POSIX_GOODIES = UnfortunatelyYes
        DEFAULT_HELP_FORMAT = html
+       NO_D_INO_IN_DIRENT = YesPlease
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -D_USE_32BIT_TIME_T -DNOGDI -Icompat -Icompat/win32
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/winansi.o \
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);
index 019026efcbc5876d7aa3890eab5078a8bfbe2f7c..7a6e1d797a1ec95a17ad2a759acb85cbe25e9f10 100644 (file)
 #        source ~/.git-completion.sh
 #    3) Consider changing your PS1 to also show the current branch,
 #       see git-prompt.sh for details.
+#
+# If you use complex aliases of form '!f() { ... }; f', you can use the null
+# command ':' as the first command in the function body to declare the desired
+# completion style.  For example '!f() { : git commit ; ... }; f' will
+# tell the completion to use commit completion.  This also works with aliases
+# of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
@@ -781,6 +787,10 @@ __git_aliased_command ()
                -*)     : option ;;
                *=*)    : setting env ;;
                git)    : git itself ;;
+               \(\))   : skip parens of shell function definition ;;
+               {)      : skip start of shell helper function ;;
+               :)      : skip null command ;;
+               \'*)    : skip opening quote after sh -c ;;
                *)
                        echo "$word"
                        return
index b4c9376a2cf1611d777e3f995cae9f20e6616e4d..08cf246bbb1c8306c20cd396ae6e6e80c3b8cbe7 100755 (executable)
@@ -516,7 +516,7 @@ then
 
        case "$no_checkout" in
        '')
-               test "z$quiet" = z -a "z$no_progress" = z && v=-v || v=
+               test "z$quiet" = z && test "z$no_progress" = z && v=-v || v=
                git read-tree -m -u $v HEAD HEAD
        esac
 fi
index 5cafe2eb7715123ba0d748be308f35731ea89ddb..934505bab9e6cf04a3536010ac31b1795c852c9f 100755 (executable)
@@ -51,7 +51,7 @@ run_status () {
                export GIT_INDEX_FILE
        fi
 
-       if test "$status_only" = "t" -o "$use_status_color" = "t"; then
+       if test "$status_only" = "t" || test "$use_status_color" = "t"; then
                color=
        else
                color=--nocolor
@@ -296,7 +296,7 @@ t,,,[1-9]*)
        die "No paths with -i does not make sense." ;;
 esac
 
-if test ! -z "$templatefile" -a -z "$log_given"
+if test ! -z "$templatefile" && test -z "$log_given"
 then
        if test ! -f "$templatefile"
        then
index 7e40f40c78e2b040ae5b85adb27ebd4828f42b4d..52f2aafb9d720723c1dc08e870f38ef495198fa3 100755 (executable)
@@ -161,7 +161,7 @@ merge_name () {
                        return
                fi
        fi
-       if test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
+       if test "$remote" = "FETCH_HEAD" && test -r "$GIT_DIR/FETCH_HEAD"
        then
                sed -e 's/      not-for-merge   /               /' -e 1q \
                        "$GIT_DIR/FETCH_HEAD"
@@ -527,7 +527,7 @@ do
                git diff-files --name-only
                git ls-files --unmerged
            } | wc -l`
-           if test $best_cnt -le 0 -o $cnt -le $best_cnt
+           if test $best_cnt -le 0 || test $cnt -le $best_cnt
            then
                best_strategy=$strategy
                best_cnt=$cnt
index f312405a251ddc42f0483fb3d1ccc92d8a266fd1..96e3fed326abeb293e9ef398989bce1054cbf855 100755 (executable)
@@ -76,8 +76,8 @@ case ",$all_into_one," in
                                existing="$existing $e"
                        fi
                done
-               if test -n "$existing" -a -n "$unpack_unreachable" -a \
-                       -n "$remove_redundant"
+               if test -n "$existing" && test -n "$unpack_unreachable" && \
+                       test -n "$remove_redundant"
                then
                        # This may have arbitrary user arguments, so we
                        # have to protect it against whitespace splitting
index 48d0fc971f9e7ec3b4b15608950217ad71c5ba94..70fdc27b724f28793c6a4bff713957a22689b78b 100755 (executable)
@@ -76,7 +76,7 @@ case "$common" in
                        2>/dev/null || continue
                # Count the paths that are unmerged.
                cnt=$(GIT_INDEX_FILE=$G git ls-files --unmerged | wc -l)
-               if test $best_cnt -le 0 -o $cnt -le $best_cnt
+               if test $best_cnt -le 0 || test $cnt -le $best_cnt
                then
                        best=$c
                        best_cnt=$cnt
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)
diff --git a/dir.c b/dir.c
index 797805d6a1d33ef66ccb61155135cdcd1a53d9d0..e65888dfad187960bf77904708e60f532ef19e1a 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1354,8 +1354,7 @@ static int cmp_name(const void *p1, const void *p2)
        const struct dir_entry *e1 = *(const struct dir_entry **)p1;
        const struct dir_entry *e2 = *(const struct dir_entry **)p2;
 
-       return cache_name_compare(e1->name, e1->len,
-                                 e2->name, e2->len);
+       return name_compare(e1->name, e1->len, e2->name, e2->len);
 }
 
 static struct path_simplify *create_simplify(const char **pathspec)
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 4dac5e9edd75499ccf52debc847807d2fc180730..565f65293bb25ebdcb6ada262bcca9164c3ea113 100644 (file)
@@ -124,6 +124,12 @@ static char *expand_namespace(const char *raw_namespace)
        return strbuf_detach(&buf, NULL);
 }
 
+static char *git_path_from_env(const char *envvar, const char *path)
+{
+       const char *value = getenv(envvar);
+       return value ? xstrdup(value) : git_pathdup("%s", path);
+}
+
 static void setup_git_env(void)
 {
        const char *gitfile;
@@ -134,19 +140,9 @@ static void setup_git_env(void)
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
        gitfile = read_gitfile(git_dir);
        git_dir = xstrdup(gitfile ? gitfile : git_dir);
-       git_object_dir = getenv(DB_ENVIRONMENT);
-       if (!git_object_dir) {
-               git_object_dir = xmalloc(strlen(git_dir) + 9);
-               sprintf(git_object_dir, "%s/objects", git_dir);
-       }
-       git_index_file = getenv(INDEX_ENVIRONMENT);
-       if (!git_index_file) {
-               git_index_file = xmalloc(strlen(git_dir) + 7);
-               sprintf(git_index_file, "%s/index", git_dir);
-       }
-       git_graft_file = getenv(GRAFT_ENVIRONMENT);
-       if (!git_graft_file)
-               git_graft_file = git_pathdup("info/grafts");
+       git_object_dir = git_path_from_env(DB_ENVIRONMENT, "objects");
+       git_index_file = git_path_from_env(INDEX_ENVIRONMENT, "index");
+       git_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, "info/grafts");
        if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
                check_replace_refs = 0;
        namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
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 72ec520fda7124e49d41e03ea81310a231841a24..b8a58fa7a542fe166ad71c9b17a4908c81d24a4f 100644 (file)
@@ -511,7 +511,7 @@ static void filter_refs(struct fetch_pack_args *args,
                int keep = 0;
                next = ref->next;
 
-               if (!memcmp(ref->name, "refs/", 5) &&
+               if (starts_with(ref->name, "refs/") &&
                    check_refname_format(ref->name, 0))
                        ; /* trash */
                else {
diff --git a/fsck.c b/fsck.c
index bdbea2b995dd5714e8422e18c0bed08ae1a41afa..a4e8593e78c85c244d2dc8bac0cca838c14bbd4a 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -276,9 +276,9 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f
        return 0;
 }
 
-static int fsck_commit(struct commit *commit, fsck_error error_func)
+static int fsck_commit_buffer(struct commit *commit, const char *buffer,
+                             fsck_error error_func)
 {
-       const char *buffer = commit->buffer;
        unsigned char tree_sha1[20], sha1[20];
        struct commit_graft *graft;
        int parents = 0;
@@ -332,6 +332,14 @@ static int fsck_commit(struct commit *commit, fsck_error error_func)
        return 0;
 }
 
+static int fsck_commit(struct commit *commit, fsck_error error_func)
+{
+       const char *buffer = get_commit_buffer(commit, NULL);
+       int ret = fsck_commit_buffer(commit, buffer, error_func);
+       unuse_commit_buffer(commit, buffer);
+       return ret;
+}
+
 static int fsck_tag(struct tag *tag, fsck_error error_func)
 {
        struct object *tagged = tag->tagged;
index af4d04c3be87423d3c9ef6078766fff679411766..1e0d602f4b156929b74d10546cf5a5ba0f8518dd 100755 (executable)
@@ -408,7 +408,7 @@ bisect_replay () {
        bisect_reset
        while read git bisect command rev
        do
-               test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
+               test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
                if test "$git" = "git-bisect"
                then
                        rev="$command"
index d29e1dff08de9caa2b37eff71bffe7d37225796e..0b53c9a4af3c7e1d5611065b35ef38f3f1f5e5be 100644 (file)
@@ -347,7 +347,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 +376,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 +613,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,6 +729,17 @@ 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
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 6a8907e7b327d066c616f8f77c2bc2096d618e06..b186329d28c07825992fad039746df8563a92c3b 100755 (executable)
@@ -1558,7 +1558,11 @@ proc rescan_stage2 {fd after} {
 
        set rescan_active 2
        ui_status [mc "Scanning for modified files ..."]
-       set fd_di [git_read diff-index --cached -z [PARENT]]
+       if {[git-version >= "1.7.2"]} {
+               set fd_di [git_read diff-index --cached --ignore-submodules=dirty -z [PARENT]]
+       } else {
+               set fd_di [git_read diff-index --cached -z [PARENT]]
+       }
        set fd_df [git_read diff-files -z]
 
        fconfigure $fd_di -blocking 0 -translation binary -encoding binary
index 30d9a797769b49cdd9d42fba925d8fa8ecbffb21..b0a5180af71ac04c58d2dd44664df98b67d3a770 100644 (file)
@@ -287,6 +287,9 @@ proc start_show_diff {cont_info {add_opts {}}} {
        if {$w eq $ui_index} {
                lappend cmd diff-index
                lappend cmd --cached
+               if {[git-version >= "1.7.2"]} {
+                       lappend cmd --ignore-submodules=dirty
+               }
        } elseif {$w eq $ui_workdir} {
                if {[string first {U} $m] >= 0} {
                        lappend cmd diff
index d08dc92589b366746965a0eab0727ce2befc579d..9a046b75d1dd810202d784d3fffe8afe6dd1963c 100755 (executable)
@@ -205,7 +205,7 @@ checkout_staged_file () {
                "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
                : '\([^ ]*\)    ')
 
-       if test $? -eq 0 -a -n "$tmpfile"
+       if test $? -eq 0 && test -n "$tmpfile"
        then
                mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
        else
@@ -256,7 +256,7 @@ merge_file () {
        checkout_staged_file 2 "$MERGED" "$LOCAL"
        checkout_staged_file 3 "$MERGED" "$REMOTE"
 
-       if test -z "$local_mode" -o -z "$remote_mode"
+       if test -z "$local_mode" || test -z "$remote_mode"
        then
                echo "Deleted merge conflict for '$MERGED':"
                describe_file "$local_mode" "local" "$LOCAL"
index 7bb0f7313bc9d4d2ad1ae43ec68afe761334a59a..ff132b2117c5ed7529d6b1237a9f26069cb4ef8a 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -1238,7 +1238,7 @@ def edit_template(self, template_file):
             if response == 'n':
                 return False
 
-    def get_diff_description(self, editedFiles):
+    def get_diff_description(self, editedFiles, filesToAdd):
         # diff
         if os.environ.has_key("P4DIFF"):
             del(os.environ["P4DIFF"])
@@ -1258,7 +1258,7 @@ def get_diff_description(self, editedFiles):
                 newdiff += "+" + line
             f.close()
 
-        return diff + newdiff
+        return (diff + newdiff).replace('\r\n', '\n')
 
     def applyCommit(self, id):
         """Apply one commit, return True if it succeeded."""
@@ -1422,10 +1422,10 @@ def applyCommit(self, id):
         separatorLine = "######## everything below this line is just the diff #######\n"
         if not self.prepare_p4_only:
             submitTemplate += separatorLine
-            submitTemplate += self.get_diff_description(editedFiles)
+            submitTemplate += self.get_diff_description(editedFiles, filesToAdd)
 
         (handle, fileName) = tempfile.mkstemp()
-        tmpFile = os.fdopen(handle, "w+")
+        tmpFile = os.fdopen(handle, "w+b")
         if self.isWindows:
             submitTemplate = submitTemplate.replace("\n", "\r\n")
         tmpFile.write(submitTemplate)
@@ -1475,9 +1475,9 @@ def applyCommit(self, id):
             tmpFile = open(fileName, "rb")
             message = tmpFile.read()
             tmpFile.close()
-            submitTemplate = message[:message.index(separatorLine)]
             if self.isWindows:
-                submitTemplate = submitTemplate.replace("\r\n", "\n")
+                message = message.replace("\r\n", "\n")
+            submitTemplate = message[:message.index(separatorLine)]
             p4_write_pipe(['submit', '-i'], submitTemplate)
 
             if self.preserveUser:
index f267d8b6c3eea5c557e63814e40a9ef38b776354..7e1eda008815cca80561d6013d41479ea8c90f4a 100644 (file)
@@ -1013,7 +1013,7 @@ then
        git rev-list $revisions |
        while read rev
        do
-               if test -f "$rewritten"/$rev -a "$(sane_grep "$rev" "$state_dir"/not-cherry-picks)" = ""
+               if test -f "$rewritten"/$rev && test "$(sane_grep "$rev" "$state_dir"/not-cherry-picks)" = ""
                then
                        # Use -f2 because if rev-list is telling us this commit is
                        # not worthwhile, we don't want to track its multiple heads,
index 6d77b3ca9118e6fdd36615757a24c109cf0530ee..d3fb67d75bd3cdfebd048594cdb67eb179b1c435 100644 (file)
@@ -53,11 +53,12 @@ continue_merge () {
 }
 
 call_merge () {
-       cmt="$(cat "$state_dir/cmt.$1")"
+       msgnum="$1"
+       echo "$msgnum" >"$state_dir/msgnum"
+       cmt="$(cat "$state_dir/cmt.$msgnum")"
        echo "$cmt" > "$state_dir/current"
        hd=$(git rev-parse --verify HEAD)
        cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
-       msgnum=$(cat "$state_dir/msgnum")
        eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
        eval GITHEAD_$hd='$onto_name'
        export GITHEAD_$cmt GITHEAD_$hd
index abd62b484cdaef8e6ab5ba366551c03959c08beb..9949db01e11959c81fae75fa018551601e6d5c3b 100755 (executable)
@@ -80,6 +80,8 @@ sub usage {
     --to-cmd                <str>  * Email To: via `<str> \$patch_path`
     --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
     --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, all.
+    --[no-]cc-cover                * Email Cc: addresses in the cover letter.
+    --[no-]to-cover                * Email To: addresses in the cover letter.
     --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on.
     --[no-]suppress-from           * Send to self. Default off.
     --[no-]chain-reply-to          * Chain In-Reply-To: fields. Default off.
@@ -195,6 +197,7 @@ sub do_edit {
 
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
+my ($cover_cc, $cover_to);
 my ($to_cmd, $cc_cmd);
 my ($smtp_server, $smtp_server_port, @smtp_server_options);
 my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
@@ -211,6 +214,8 @@ sub do_edit {
     "chainreplyto" => [\$chain_reply_to, 0],
     "suppressfrom" => [\$suppress_from, undef],
     "signedoffbycc" => [\$signed_off_by_cc, undef],
+    "cccover" => [\$cover_cc, undef],
+    "tocover" => [\$cover_to, undef],
     "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
     "validate" => [\$validate, 1],
     "multiedit" => [\$multiedit, undef],
@@ -302,6 +307,8 @@ sub signal_handler {
                    "suppress-from!" => \$suppress_from,
                    "suppress-cc=s" => \@suppress_cc,
                    "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
+                   "cc-cover|cc-cover!" => \$cover_cc,
+                   "to-cover|to-cover!" => \$cover_to,
                    "confirm=s" => \$confirm,
                    "dry-run" => \$dry_run,
                    "envelope-sender=s" => \$envelope_sender,
@@ -1481,6 +1488,15 @@ sub send_message {
        @to = (@initial_to, @to);
        @cc = (@initial_cc, @cc);
 
+       if ($message_num == 1) {
+               if (defined $cover_cc and $cover_cc) {
+                       @initial_cc = @cc;
+               }
+               if (defined $cover_to and $cover_to) {
+                       @initial_to = @to;
+               }
+       }
+
        my $message_was_sent = send_message();
 
        # set up for the next message
index e146b833d163ab58984fec3d6fb9f45b148e0171..9245abfd4263881bdd6d0f21f648b46201b52a2d 100755 (executable)
@@ -235,7 +235,7 @@ module_name()
                sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
        test -z "$name" &&
        die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$sm_path'")"
-       echo "$name"
+       printf '%s\n' "$name"
 }
 
 #
@@ -305,10 +305,10 @@ module_clone()
        b=${b%/}
 
        # Turn each leading "*/" component into "../"
-       rel=$(echo $b | sed -e 's|[^/][^/]*|..|g')
-       echo "gitdir: $rel/$a" >"$sm_path/.git"
+       rel=$(printf '%s\n' "$b" | sed -e 's|[^/][^/]*|..|g')
+       printf '%s\n' "gitdir: $rel/$a" >"$sm_path/.git"
 
-       rel=$(echo $a | sed -e 's|[^/][^/]*|..|g')
+       rel=$(printf '%s\n' "$a" | sed -e 's|[^/][^/]*|..|g')
        (clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b")
 }
 
@@ -389,11 +389,11 @@ cmd_add()
        sm_path=$2
 
        if test -z "$sm_path"; then
-               sm_path=$(echo "$repo" |
+               sm_path=$(printf '%s\n' "$repo" |
                        sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
        fi
 
-       if test -z "$repo" -o -z "$sm_path"; then
+       if test -z "$repo" || test -z "$sm_path"; then
                usage
        fi
 
@@ -450,7 +450,7 @@ Use -f if you really want to add it." >&2
        # perhaps the path exists and is already a git repo, else clone it
        if test -e "$sm_path"
        then
-               if test -d "$sm_path"/.git -o -f "$sm_path"/.git
+               if test -d "$sm_path"/.git || test -f "$sm_path"/.git
                then
                        eval_gettextln "Adding existing repo at '\$sm_path' to the index"
                else
@@ -832,7 +832,7 @@ Maybe you want to use 'update --init'?")"
                        continue
                fi
 
-               if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
+               if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git
                then
                        module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit
                        cloned_modules="$cloned_modules;$name"
@@ -857,11 +857,11 @@ Maybe you want to use 'update --init'?")"
                        die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '\$sm_path'")"
                fi
 
-               if test "$subsha1" != "$sha1" -o -n "$force"
+               if test "$subsha1" != "$sha1" || test -n "$force"
                then
                        subforce=$force
                        # If we don't already have a -f flag and the submodule has never been checked out
-                       if test -z "$subsha1" -a -z "$force"
+                       if test -z "$subsha1" && test -z "$force"
                        then
                                subforce="-f"
                        fi
@@ -1031,7 +1031,7 @@ cmd_summary() {
        then
                head=$rev
                test $# = 0 || shift
-       elif test -z "$1" -o "$1" = "HEAD"
+       elif test -z "$1" || test "$1" = "HEAD"
        then
                # before the first commit: compare with an empty tree
                head=$(git hash-object -w -t tree --stdin </dev/null)
@@ -1056,17 +1056,21 @@ cmd_summary() {
                while read mod_src mod_dst sha1_src sha1_dst status sm_path
                do
                        # Always show modules deleted or type-changed (blob<->module)
-                       test $status = D -o $status = T && echo "$sm_path" && continue
+                       if test "$status" = D || test "$status" = T
+                       then
+                               printf '%s\n' "$sm_path"
+                               continue
+                       fi
                        # Respect the ignore setting for --for-status.
                        if test -n "$for_status"
                        then
                                name=$(module_name "$sm_path")
                                ignore_config=$(get_submodule_config "$name" ignore none)
-                               test $status != A -a $ignore_config = all && continue
+                               test $status != A && test $ignore_config = all && continue
                        fi
                        # Also show added or modified modules which are checked out
                        GIT_DIR="$sm_path/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
-                       echo "$sm_path"
+                       printf '%s\n' "$sm_path"
                done
        )
 
@@ -1122,7 +1126,7 @@ cmd_summary() {
                *)
                        errmsg=
                        total_commits=$(
-                       if test $mod_src = 160000 -a $mod_dst = 160000
+                       if test $mod_src = 160000 && test $mod_dst = 160000
                        then
                                range="$sha1_src...$sha1_dst"
                        elif test $mod_src = 160000
@@ -1159,7 +1163,7 @@ cmd_summary() {
                        # i.e. deleted or changed to blob
                        test $mod_dst = 160000 && echo "$errmsg"
                else
-                       if test $mod_src = 160000 -a $mod_dst = 160000
+                       if test $mod_src = 160000 && test $mod_dst = 160000
                        then
                                limit=
                                test $summary_limit -gt 0 && limit="-$summary_limit"
@@ -1230,7 +1234,11 @@ cmd_status()
                        say "U$sha1 $displaypath"
                        continue
                fi
-               if test -z "$url" || ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
+               if test -z "$url" ||
+               {
+                       ! test -d "$sm_path"/.git &&
+                       ! test -f "$sm_path"/.git
+               }
                then
                        say "-$sha1 $displaypath"
                        continue;
@@ -1303,7 +1311,7 @@ cmd_sync()
                ./*|../*)
                        # rewrite foo/bar as ../.. to find path from
                        # submodule work tree to superproject work tree
-                       up_path="$(echo "$sm_path" | sed "s/[^/][^/]*/../g")" &&
+                       up_path="$(printf '%s\n' "$sm_path" | sed "s/[^/][^/]*/../g")" &&
                        # guarantee a trailing /
                        up_path=${up_path%/}/ &&
                        # path from submodule work tree to submodule origin repo
@@ -1399,7 +1407,7 @@ then
 fi
 
 # "--cached" is accepted only by "status" and "summary"
-if test -n "$cached" && test "$command" != status -a "$command" != summary
+if test -n "$cached" && test "$command" != status && test "$command" != summary
 then
        usage
 fi
diff --git a/git.c b/git.c
index 1537f00bc7081aa800d390aecaf6d80ab7c546c9..5b6c7611be93c535491bbc9ba027e3acbad0db8f 100644 (file)
--- a/git.c
+++ b/git.c
@@ -20,6 +20,43 @@ const char git_more_info_string[] =
 
 static struct startup_info git_startup_info;
 static int use_pager = -1;
+static char orig_cwd[PATH_MAX];
+static const char *env_names[] = {
+       GIT_DIR_ENVIRONMENT,
+       GIT_WORK_TREE_ENVIRONMENT,
+       GIT_IMPLICIT_WORK_TREE_ENVIRONMENT,
+       GIT_PREFIX_ENVIRONMENT
+};
+static char *orig_env[4];
+static int saved_environment;
+
+static void save_env(void)
+{
+       int i;
+       if (saved_environment)
+               return;
+       saved_environment = 1;
+       if (!getcwd(orig_cwd, sizeof(orig_cwd)))
+               die_errno("cannot getcwd");
+       for (i = 0; i < ARRAY_SIZE(env_names); i++) {
+               orig_env[i] = getenv(env_names[i]);
+               if (orig_env[i])
+                       orig_env[i] = xstrdup(orig_env[i]);
+       }
+}
+
+static void restore_env(void)
+{
+       int i;
+       if (*orig_cwd && chdir(orig_cwd))
+               die_errno("could not move to %s", orig_cwd);
+       for (i = 0; i < ARRAY_SIZE(env_names); i++) {
+               if (orig_env[i])
+                       setenv(env_names[i], orig_env[i], 1);
+               else
+                       unsetenv(env_names[i]);
+       }
+}
 
 static void commit_pager_choice(void) {
        switch (use_pager) {
@@ -271,6 +308,7 @@ static int handle_alias(int *argcp, const char ***argv)
  * RUN_SETUP for reading from the configuration file.
  */
 #define NEED_WORK_TREE         (1<<3)
+#define NO_SETUP               (1<<4)
 
 struct cmd_struct {
        const char *cmd;
@@ -351,7 +389,7 @@ static struct cmd_struct commands[] = {
        { "cherry", cmd_cherry, RUN_SETUP },
        { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
        { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
-       { "clone", cmd_clone },
+       { "clone", cmd_clone, NO_SETUP },
        { "column", cmd_column, RUN_SETUP_GENTLY },
        { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
        { "commit-tree", cmd_commit_tree, RUN_SETUP },
@@ -377,8 +415,8 @@ static struct cmd_struct commands[] = {
        { "hash-object", cmd_hash_object },
        { "help", cmd_help },
        { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
-       { "init", cmd_init_db },
-       { "init-db", cmd_init_db },
+       { "init", cmd_init_db, NO_SETUP },
+       { "init-db", cmd_init_db, NO_SETUP },
        { "log", cmd_log, RUN_SETUP },
        { "ls-files", cmd_ls_files, RUN_SETUP },
        { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
@@ -440,6 +478,7 @@ static struct cmd_struct commands[] = {
        { "upload-archive", cmd_upload_archive },
        { "upload-archive--writer", cmd_upload_archive_writer },
        { "var", cmd_var, RUN_SETUP_GENTLY },
+       { "verify-commit", cmd_verify_commit, RUN_SETUP },
        { "verify-pack", cmd_verify_pack },
        { "verify-tag", cmd_verify_tag, RUN_SETUP },
        { "version", cmd_version },
@@ -483,6 +522,10 @@ static void handle_builtin(int argc, const char **argv)
                struct cmd_struct *p = commands+i;
                if (strcmp(p->cmd, cmd))
                        continue;
+               if (saved_environment && (p->option & NO_SETUP)) {
+                       restore_env();
+                       break;
+               }
                exit(run_builtin(p, argc, argv));
        }
 }
@@ -538,7 +581,10 @@ static int run_argv(int *argcp, const char ***argv)
                 * of overriding "git log" with "git show" by having
                 * alias.log = show
                 */
-               if (done_alias || !handle_alias(argcp, argv))
+               if (done_alias)
+                       break;
+               save_env();
+               if (!handle_alias(argcp, argv))
                        break;
                done_alias = 1;
        }
index 90764e8948f1f95aaddfb381664ecede328bc863..c8df35dee58aed6ba652ee9ab998f405539991eb 100755 (executable)
@@ -2585,6 +2585,7 @@ proc makewindow {} {
     bind $fstring <Key-Return> {dofind 1 1}
     bind $sha1entry <Key-Return> {gotocommit; break}
     bind $sha1entry <<PasteSelection>> clearsha1
+    bind $sha1entry <<Paste>> clearsha1
     bind $cflist <1> {sel_flist %W %x %y; break}
     bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
     bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
@@ -2786,7 +2787,7 @@ proc savestuff {w} {
     global mainheadcirclecolor workingfilescirclecolor indexcirclecolor
     global linkfgcolor circleoutlinecolor
     global autoselect autosellen extdifftool perfile_attrs markbgcolor use_ttk
-    global hideremotes want_ttk maxrefs
+    global hideremotes want_ttk maxrefs visiblerefs
     global config_file config_file_tmp
 
     if {$stuffsaved} return
@@ -2812,6 +2813,7 @@ proc savestuff {w} {
        puts $f [list set autosellen $autosellen]
        puts $f [list set showneartags $showneartags]
        puts $f [list set maxrefs $maxrefs]
+       puts $f [list set visiblerefs $visiblerefs]
        puts $f [list set hideremotes $hideremotes]
        puts $f [list set showlocalchanges $showlocalchanges]
        puts $f [list set datetimeformat $datetimeformat]
@@ -3492,10 +3494,20 @@ proc flist_hl {only} {
 }
 
 proc gitknewtmpdir {} {
-    global diffnum gitktmpdir gitdir
+    global diffnum gitktmpdir gitdir env
 
     if {![info exists gitktmpdir]} {
-       set gitktmpdir [file join $gitdir [format ".gitk-tmp.%s" [pid]]]
+       if {[info exists env(GITK_TMPDIR)]} {
+           set tmpdir $env(GITK_TMPDIR)
+       } elseif {[info exists env(TMPDIR)]} {
+           set tmpdir $env(TMPDIR)
+       } else {
+           set tmpdir $gitdir
+       }
+       set gitktmpformat [file join $tmpdir ".gitk-tmp.XXXXXX"]
+       if {[catch {set gitktmpdir [exec mktemp -d $gitktmpformat]}]} {
+           set gitktmpdir [file join $gitdir [format ".gitk-tmp.%s" [pid]]]
+       }
        if {[catch {file mkdir $gitktmpdir} err]} {
            error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
            unset gitktmpdir
@@ -3870,7 +3882,7 @@ proc read_line_source {fd inst} {
            set id $nullid2
        }
        if {[commitinview $id $curview]} {
-           selectline [rowofcommit $id] 1 [list $fname $lnum]
+           selectline [rowofcommit $id] 1 [list $fname $lnum] 1
        } else {
            error_popup [mc "That line comes from commit %s, \
                             which is not in this view" [shortids $id]]
@@ -5205,11 +5217,15 @@ proc dohidelocalchanges {} {
 # spawn off a process to do git diff-index --cached HEAD
 proc dodiffindex {} {
     global lserial showlocalchanges vfilelimit curview
-    global hasworktree
+    global hasworktree git_version
 
     if {!$showlocalchanges || !$hasworktree} return
     incr lserial
-    set cmd "|git diff-index --cached HEAD"
+    if {[package vcompare $git_version "1.7.2"] >= 0} {
+       set cmd "|git diff-index --cached --ignore-submodules=dirty HEAD"
+    } else {
+       set cmd "|git diff-index --cached HEAD"
+    }
     if {$vfilelimit($curview) ne {}} {
        set cmd [concat $cmd -- $vfilelimit($curview)]
     }
@@ -7020,7 +7036,7 @@ proc viewnextline {dir} {
 # add a list of tag or branch names at position pos
 # returns the number of names inserted
 proc appendrefs {pos ids var} {
-    global ctext linknum curview $var maxrefs mainheadid
+    global ctext linknum curview $var maxrefs visiblerefs mainheadid
 
     if {[catch {$ctext index $pos}]} {
        return 0
@@ -7041,14 +7057,14 @@ proc appendrefs {pos ids var} {
     if {[llength $tags] > $maxrefs} {
        # If we are displaying heads, and there are too many,
        # see if there are some important heads to display.
-       # Currently this means "master" and the current head.
+       # Currently that are the current head and heads listed in $visiblerefs option
        set itags {}
        if {$var eq "idheads"} {
            set utags {}
            foreach ti $tags {
                set hname [lindex $ti 0]
                set id [lindex $ti 1]
-               if {($hname eq "master" || $id eq $mainheadid) &&
+               if {([lsearch -exact $visiblerefs $hname] != -1 || $id eq $mainheadid) &&
                    [llength $itags] < $maxrefs} {
                    lappend itags $ti
                } else {
@@ -7161,7 +7177,7 @@ proc make_idmark {id} {
     $canv raise $t
 }
 
-proc selectline {l isnew {desired_loc {}}} {
+proc selectline {l isnew {desired_loc {}} {switch_to_patch 0}} {
     global canv ctext commitinfo selectedline
     global canvy0 linespc parents children curview
     global currentid sha1entry
@@ -7187,6 +7203,10 @@ proc selectline {l isnew {desired_loc {}}} {
        setcanvscroll
     }
 
+    if {$cmitmode ne "patch" && $switch_to_patch} {
+        set cmitmode "patch"
+    }
+
     set y [expr {$canvy0 + $l * $linespc}]
     set ymax [lindex [$canv cget -scrollregion] 3]
     set ytop [expr {$y - $linespc - 1}]
@@ -7705,7 +7725,7 @@ proc addtocflist {ids} {
 }
 
 proc diffcmd {ids flags} {
-    global log_showroot nullid nullid2
+    global log_showroot nullid nullid2 git_version
 
     set i [lsearch -exact $ids $nullid]
     set j [lsearch -exact $ids $nullid2]
@@ -7726,6 +7746,9 @@ proc diffcmd {ids flags} {
            }
        }
     } elseif {$j >= 0} {
+       if {[package vcompare $git_version "1.7.2"] >= 0} {
+           set flags "$flags --ignore-submodules=dirty"
+       }
        set cmd [concat | git diff-index --cached $flags]
        if {[llength $ids] > 1} {
            # comparing index with specific revision
@@ -11575,7 +11598,29 @@ proc prefsok {} {
 proc formatdate {d} {
     global datetimeformat
     if {$d ne {}} {
-       set d [clock format [lindex $d 0] -format $datetimeformat]
+       # If $datetimeformat includes a timezone, display in the
+       # timezone of the argument.  Otherwise, display in local time.
+       if {[string match {*%[zZ]*} $datetimeformat]} {
+           if {[catch {set d [clock format [lindex $d 0] -timezone [lindex $d 1] -format $datetimeformat]}]} {
+               # Tcl < 8.5 does not support -timezone.  Emulate it by
+               # setting TZ (e.g. TZ=<-0430>+04:30).
+               global env
+               if {[info exists env(TZ)]} {
+                   set savedTZ $env(TZ)
+               }
+               set zone [lindex $d 1]
+               set sign [string map {+ - - +} [string index $zone 0]]
+               set env(TZ) <$zone>$sign[string range $zone 1 2]:[string range $zone 3 4]
+               set d [clock format [lindex $d 0] -format $datetimeformat]
+               if {[info exists savedTZ]} {
+                   set env(TZ) $savedTZ
+               } else {
+                   unset env(TZ)
+               }
+           }
+       } else {
+           set d [clock format [lindex $d 0] -format $datetimeformat]
+       }
     }
     return $d
 }
@@ -12001,6 +12046,7 @@ set wrapcomment "none"
 set showneartags 1
 set hideremotes 0
 set maxrefs 20
+set visiblerefs {"master"}
 set maxlinelen 200
 set showlocalchanges 1
 set limitdiffs 1
diff --git a/gitk-git/po/vi.po b/gitk-git/po/vi.po
new file mode 100644 (file)
index 0000000..4dfe125
--- /dev/null
@@ -0,0 +1,1351 @@
+# Vietnamese translations for gitk package.
+# Bản dịch tiếng Việt cho gói gitk.
+# This file is distributed under the same license as the gitk package.
+# Trần Ngọc Quân <vnwildman@gmail.com>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk @@GIT_VERSION@@\n"
+"Report-Msgid-Bugs-To: Paul Mackerras <paulus@samba.org>\n"
+"POT-Creation-Date: 2013-12-14 09:24+0700\n"
+"PO-Revision-Date: 2013-12-14 14:40+0700\n"
+"Last-Translator: Trần Ngọc Quân <vnwildman@gmail.com>\n"
+"Language-Team: Vietnamese <translation-team-vi@lists.sourceforge.net>\n"
+"Language: vi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: gitk:140
+msgid "Couldn't get list of unmerged files:"
+msgstr "Không thể lấy danh sách các tập-tin chưa được hòa trộn:"
+
+#: gitk:212 gitk:2353
+msgid "Color words"
+msgstr "Tô màu chữ"
+
+#: gitk:217 gitk:2353 gitk:8103 gitk:8136
+msgid "Markup words"
+msgstr "Đánh dấu chữ"
+
+#: gitk:322
+msgid "Error parsing revisions:"
+msgstr "Gặp lỗi khi phân tích điểm xét duyệt:"
+
+#: gitk:378
+msgid "Error executing --argscmd command:"
+msgstr "Gặp lỗi khi thực hiện lệnh --argscmd:"
+
+#: gitk:391
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Chưa chọn tập tin: --merge đã chỉ định nhưng không có tập tin chưa hòa trộn."
+
+#: gitk:394
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Chưa chọn tập tin: --merge đã chỉ định nhưng không có tập tin chưa hòa trộn "
+"trong giới hạn tập tin."
+
+#: gitk:416 gitk:564
+msgid "Error executing git log:"
+msgstr "Gặp lỗi khi thực hiện lệnh git log:"
+
+#: gitk:434 gitk:580
+msgid "Reading"
+msgstr "Đang đọc"
+
+#: gitk:494 gitk:4429
+msgid "Reading commits..."
+msgstr "Đang đọc các lần chuyển giao..."
+
+#: gitk:497 gitk:1635 gitk:4432
+msgid "No commits selected"
+msgstr "Chưa chọn các lần chuyển giao"
+
+#: gitk:1509
+msgid "Can't parse git log output:"
+msgstr "Không thể phân tích kết xuất từ lệnh git log:"
+
+#: gitk:1738
+msgid "No commit information available"
+msgstr "Không có thông tin về lần chuyển giao nào"
+
+#: gitk:1895
+msgid "mc"
+msgstr "mc"
+
+#: gitk:1930 gitk:4222 gitk:9552 gitk:11122 gitk:11401
+msgid "OK"
+msgstr "Đồng ý"
+
+#: gitk:1932 gitk:4224 gitk:9079 gitk:9158 gitk:9274 gitk:9323 gitk:9554
+#: gitk:11123 gitk:11402
+msgid "Cancel"
+msgstr "Thôi"
+
+#: gitk:2067
+msgid "Update"
+msgstr "Cập nhật"
+
+#: gitk:2068
+msgid "Reload"
+msgstr "Tải lại"
+
+#: gitk:2069
+msgid "Reread references"
+msgstr "Đọc lại tham chiếu"
+
+#: gitk:2070
+msgid "List references"
+msgstr "Liệt kê các tham chiếu"
+
+#: gitk:2072
+msgid "Start git gui"
+msgstr "Khởi chạy git gui"
+
+#: gitk:2074
+msgid "Quit"
+msgstr "Thoát"
+
+#: gitk:2066
+msgid "File"
+msgstr "Chính"
+
+#: gitk:2078
+msgid "Preferences"
+msgstr "Cá nhân hóa"
+
+#: gitk:2077
+msgid "Edit"
+msgstr "Chỉnh sửa"
+
+#: gitk:2082
+msgid "New view..."
+msgstr "Thêm trình bày mới..."
+
+#: gitk:2083
+msgid "Edit view..."
+msgstr "Sửa cách trình bày..."
+
+#: gitk:2084
+msgid "Delete view"
+msgstr "Xóa cách trình bày"
+
+#: gitk:2086
+msgid "All files"
+msgstr "Mọi tập tin"
+
+#: gitk:2081 gitk:3975
+msgid "View"
+msgstr "Trình bày"
+
+#: gitk:2091 gitk:2101 gitk:2945
+msgid "About gitk"
+msgstr "Giới thiệu về gitk"
+
+#: gitk:2092 gitk:2106
+msgid "Key bindings"
+msgstr "Tổ hợp phím"
+
+#: gitk:2090 gitk:2105
+msgid "Help"
+msgstr "Trợ giúp"
+
+#: gitk:2183 gitk:8535
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:2227
+msgid "Row"
+msgstr "Hàng"
+
+#: gitk:2265
+msgid "Find"
+msgstr "Tìm"
+
+#: gitk:2266
+msgid "next"
+msgstr "tiếp"
+
+#: gitk:2267
+msgid "prev"
+msgstr "trước"
+
+#: gitk:2268
+msgid "commit"
+msgstr "lần chuyển giao"
+
+#: gitk:2271 gitk:2273 gitk:4590 gitk:4613 gitk:4637 gitk:6653 gitk:6725
+#: gitk:6810
+msgid "containing:"
+msgstr "có chứa:"
+
+#: gitk:2274 gitk:3457 gitk:3462 gitk:4666
+msgid "touching paths:"
+msgstr "đang chạm đường dẫn:"
+
+#: gitk:2275 gitk:4680
+msgid "adding/removing string:"
+msgstr "thêm/gỡ bỏ chuỗi:"
+
+#: gitk:2276 gitk:4682
+msgid "changing lines matching:"
+msgstr "những dòng thay đổi khớp mẫu:"
+
+#: gitk:2285 gitk:2287 gitk:4669
+msgid "Exact"
+msgstr "Chính xác"
+
+#: gitk:2287 gitk:4757 gitk:6621
+msgid "IgnCase"
+msgstr "BquaHt"
+
+#: gitk:2287 gitk:4639 gitk:4755 gitk:6617
+msgid "Regexp"
+msgstr "BTCQ"
+
+#: gitk:2289 gitk:2290 gitk:4777 gitk:4807 gitk:4814 gitk:6746 gitk:6814
+msgid "All fields"
+msgstr "Mọi trường"
+
+#: gitk:2290 gitk:4774 gitk:4807 gitk:6684
+msgid "Headline"
+msgstr "Nội dung chính"
+
+#: gitk:2291 gitk:4774 gitk:6684 gitk:6814 gitk:7283
+msgid "Comments"
+msgstr "Ghi chú"
+
+#: gitk:2291 gitk:4774 gitk:4779 gitk:4814 gitk:6684 gitk:7218 gitk:8713
+#: gitk:8728
+msgid "Author"
+msgstr "Tác giả"
+
+#: gitk:2291 gitk:4774 gitk:6684 gitk:7220
+msgid "Committer"
+msgstr "Người chuyển giao"
+
+#: gitk:2322
+msgid "Search"
+msgstr "Tìm kiếm"
+
+#: gitk:2330
+msgid "Diff"
+msgstr "So sánh"
+
+#: gitk:2332
+msgid "Old version"
+msgstr "Phiên bản cũ"
+
+#: gitk:2334
+msgid "New version"
+msgstr "Phiên bản mới"
+
+#: gitk:2336
+msgid "Lines of context"
+msgstr "Các dòng của nội dung"
+
+#: gitk:2346
+msgid "Ignore space change"
+msgstr "Không xét đến thay đổi do khoảng trắng"
+
+#: gitk:2350 gitk:2352 gitk:7842 gitk:8089
+msgid "Line diff"
+msgstr "Khác biệt theo dòng"
+
+#: gitk:2417
+msgid "Patch"
+msgstr "Vá"
+
+#: gitk:2419
+msgid "Tree"
+msgstr "Cây"
+
+#: gitk:2577 gitk:2597
+msgid "Diff this -> selected"
+msgstr "So sánh cái này -> cái đã chọn"
+
+#: gitk:2578 gitk:2598
+msgid "Diff selected -> this"
+msgstr "So sánh cái đã chọn -> cái này"
+
+#: gitk:2579 gitk:2599
+msgid "Make patch"
+msgstr "Tạo miếng vá"
+
+#: gitk:2580 gitk:9137
+msgid "Create tag"
+msgstr "Tạo thẻ"
+
+#: gitk:2581 gitk:9254
+msgid "Write commit to file"
+msgstr "Ghi lần chuyển giao ra tập tin"
+
+#: gitk:2582 gitk:9311
+msgid "Create new branch"
+msgstr "Tạo nhánh mới"
+
+#: gitk:2583
+msgid "Cherry-pick this commit"
+msgstr "Cherry-pick lần chuyển giao này"
+
+#: gitk:2584
+msgid "Reset HEAD branch to here"
+msgstr "Đặt lại HEAD của nhánh vào đây"
+
+#: gitk:2585
+msgid "Mark this commit"
+msgstr "Đánh dấu lần chuyển giao này"
+
+#: gitk:2586
+msgid "Return to mark"
+msgstr "Quay lại vị trí dấu"
+
+#: gitk:2587
+msgid "Find descendant of this and mark"
+msgstr "Tìm con cháu của cái này và cái đã đánh dấu"
+
+#: gitk:2588
+msgid "Compare with marked commit"
+msgstr "So sánh với lần chuyển giao đã đánh dấu"
+
+#: gitk:2589 gitk:2600
+msgid "Diff this -> marked commit"
+msgstr "So sánh cái này -> lần chuyển giao đã đánh dấu"
+
+#: gitk:2590 gitk:2601
+msgid "Diff marked commit -> this"
+msgstr "So sánh lần chuyển giao đã đánh dấu -> cái này"
+
+#: gitk:2591
+msgid "Revert this commit"
+msgstr "Hoàn lại lần chuyển giao này"
+
+#: gitk:2607
+msgid "Check out this branch"
+msgstr "Checkout nhánh này"
+
+#: gitk:2608
+msgid "Remove this branch"
+msgstr "Gỡ bỏ nhánh này"
+
+#: gitk:2615
+msgid "Highlight this too"
+msgstr "Cũng tô sáng nó"
+
+#: gitk:2616
+msgid "Highlight this only"
+msgstr "Chỉ tô sáng cái này"
+
+#: gitk:2617
+msgid "External diff"
+msgstr "diff từ bên ngoài"
+
+#: gitk:2618
+msgid "Blame parent commit"
+msgstr "Xem công trạng lần chuyển giao cha mẹ"
+
+#: gitk:2625
+msgid "Show origin of this line"
+msgstr "Hiển thị nguyên gốc của dòng này"
+
+#: gitk:2626
+msgid "Run git gui blame on this line"
+msgstr "Chạy lệnh git gui blame cho dòng này"
+
+#: gitk:2947
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright © 2005-2011 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - phần mềm xem các lần chuyển giao dành cho git\n"
+"\n"
+"Bản quyền © 2005-2011 Paul Mackerras\n"
+"\n"
+"Dùng và phân phối lại phần mềm này theo các điều khoản của Giấy Phép Công GNU"
+
+#: gitk:2955 gitk:3020 gitk:9738
+msgid "Close"
+msgstr "Đóng"
+
+#: gitk:2976
+msgid "Gitk key bindings"
+msgstr "Tổ hợp phím gitk"
+
+#: gitk:2979
+msgid "Gitk key bindings:"
+msgstr "Tổ hợp phím gitk:"
+
+#: gitk:2981
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tThoát"
+
+#: gitk:2982
+#, tcl-format
+msgid "<%s-W>\t\tClose window"
+msgstr "<%s-W>\t\tĐóng cửa sổ"
+
+#: gitk:2983
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tChuyển đến lần chuyển giao đầu tiên"
+
+#: gitk:2984
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tChuyển đến lần chuyển giao cuối"
+
+#: gitk:2985
+msgid "<Up>, p, k\tMove up one commit"
+msgstr "<Up>, p, k\tDi chuyển lên một lần chuyển giao"
+
+#: gitk:2986
+msgid "<Down>, n, j\tMove down one commit"
+msgstr "<Down>, n, j\tDi chuyển xuống một lần chuyển giao"
+
+#: gitk:2987
+msgid "<Left>, z, h\tGo back in history list"
+msgstr "<Left>, z, h\tQuay trở lại danh sách lịch sử"
+
+#: gitk:2988
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\tDi chuyển tiếp trong danh sách lịch sử"
+
+#: gitk:2989
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tDi chuyển lên một trang trong danh sách lần chuyển giao"
+
+#: gitk:2990
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tDi chuyển xuống một trang trong danh sách lần chuyển giao"
+
+#: gitk:2991
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tCuộn lên trên cùng của danh sách lần chuyển giao"
+
+#: gitk:2992
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tCuộn xuống dưới cùng của danh sách lần chuyển giao"
+
+#: gitk:2993
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tCuộn danh sách lần chuyển giao lên một dòng"
+
+#: gitk:2994
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tCuộn danh sách lần chuyển giao xuống một dòng"
+
+#: gitk:2995
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tCuộn danh sách lần chuyển giao lên một trang"
+
+#: gitk:2996
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tCuộn danh sách lần chuyển giao xuống một trang"
+
+#: gitk:2997
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\tTìm về phía sau (hướng lên trên, lần chuyển giao sau này)"
+
+#: gitk:2998
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr ""
+"<Shift-Down>\tTìm về phía trước (hướng xuống dưới, lần chuyển giao trước đây)"
+
+#: gitk:2999
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tCuộn phần trình bày diff lên một trang"
+
+#: gitk:3000
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tCuộn phần trình bày diff lên một trang"
+
+#: gitk:3001
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\tCuộn phần trình bày diff xuống một trang"
+
+#: gitk:3002
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tCuộn phần trình bày diff lên 18 dòng"
+
+#: gitk:3003
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tCuộn phần trình bày diff xuống 18 dòng"
+
+#: gitk:3004
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tTìm kiếm"
+
+#: gitk:3005
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tDi chuyển đến chỗ gặp kế tiếp"
+
+#: gitk:3006
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\t\tDi chuyển đến chỗ gặp kế tiếp"
+
+#: gitk:3007
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tĐưa con trỏ chuột vào ô tìm kiếm"
+
+#: gitk:3008
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tDi chuyển đến chỗ gặp kế trước"
+
+#: gitk:3009
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tCuộn phần trình bày diff sang tập-tin kế"
+
+#: gitk:3010
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tTìm đến chỗ khác biệt kế tiếp"
+
+#: gitk:3011
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tTìm đến chỗ khác biệt kế trước"
+
+#: gitk:3012
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tTăng cỡ chữ"
+
+#: gitk:3013
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tTăng cỡ chữ"
+
+#: gitk:3014
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tGiảm cỡ chữ"
+
+#: gitk:3015
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tGiảm cỡ chữ"
+
+#: gitk:3016
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tCập nhật"
+
+#: gitk:3471 gitk:3480
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Gặp lỗi khi tạo thư mục tạm %s:"
+
+#: gitk:3493
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Lỗi chào hỏi \"%s\" từ %s:"
+
+#: gitk:3556
+msgid "command failed:"
+msgstr "lệnh gặp lỗi:"
+
+#: gitk:3705
+msgid "No such commit"
+msgstr "Không có lần chuyển giao như vậy"
+
+#: gitk:3719
+msgid "git gui blame: command failed:"
+msgstr "git gui blame: lệnh gặp lỗi:"
+
+#: gitk:3750
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Không thể độc đầu của hòa trộn: %s"
+
+# tcl-format
+#: gitk:3758
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Gặp lỗi khi đọc chỉ mục: %s"
+
+#: gitk:3783
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Không thể khởi chạy git blame: %s"
+
+#: gitk:3786 gitk:6652
+msgid "Searching"
+msgstr "Đang tìm kiếm"
+
+#: gitk:3818
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Gặp lỗi khi chạy git blame: %s"
+
+#: gitk:3846
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "Dòng đến từ lần chuyển giao %s, cái mà không trong trình bày này"
+
+#: gitk:3860
+msgid "External diff viewer failed:"
+msgstr "Bộ trình bày diff từ bên ngoài gặp lỗi:"
+
+#: gitk:3978
+msgid "Gitk view definition"
+msgstr "Định nghĩa cách trình bày gitk"
+
+#: gitk:3982
+msgid "Remember this view"
+msgstr "Nhớ cách trình bày này"
+
+#: gitk:3983
+msgid "References (space separated list):"
+msgstr "Tham chiếu (danh sách ngăn cách bằng dấu cách):"
+
+#: gitk:3984
+msgid "Branches & tags:"
+msgstr "Nhánh & thẻ:"
+
+#: gitk:3985
+msgid "All refs"
+msgstr "Mọi tham chiếu"
+
+#: gitk:3986
+msgid "All (local) branches"
+msgstr "Mọi nhánh (nội bộ)"
+
+#: gitk:3987
+msgid "All tags"
+msgstr "Mọi thẻ"
+
+#: gitk:3988
+msgid "All remote-tracking branches"
+msgstr "Mọi nhánh remote-tracking"
+
+#: gitk:3989
+msgid "Commit Info (regular expressions):"
+msgstr "Thông tin chuyển giao (biểu thức chính quy):"
+
+#: gitk:3990
+msgid "Author:"
+msgstr "Tác giả:"
+
+#: gitk:3991
+msgid "Committer:"
+msgstr "Người chuyển giao:"
+
+#: gitk:3992
+msgid "Commit Message:"
+msgstr "Chú thích của lần chuyển giao:"
+
+#: gitk:3993
+msgid "Matches all Commit Info criteria"
+msgstr "Khớp mọi điều kiện Thông tin Chuyển giao"
+
+#: gitk:3994
+msgid "Changes to Files:"
+msgstr "Đổi thành Tập tin:"
+
+#: gitk:3995
+msgid "Fixed String"
+msgstr "Chuỗi cố định"
+
+#: gitk:3996
+msgid "Regular Expression"
+msgstr "Biểu thức chính quy"
+
+#: gitk:3997
+msgid "Search string:"
+msgstr "Chuỗi tìm kiếm:"
+
+#: gitk:3998
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr ""
+"Ngày chuyển giao (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+
+#: gitk:3999
+msgid "Since:"
+msgstr "Kể từ:"
+
+#: gitk:4000
+msgid "Until:"
+msgstr "Đến:"
+
+#: gitk:4001
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Giới hạn và/hoặc bỏ số của điểm xét (số nguyên âm):"
+
+#: gitk:4002
+msgid "Number to show:"
+msgstr "Số lượng hiển thị:"
+
+#: gitk:4003
+msgid "Number to skip:"
+msgstr "Số lượng sẽ bỏ qua:"
+
+#: gitk:4004
+msgid "Miscellaneous options:"
+msgstr "Tuỳ chọn hỗn hợp:"
+
+#: gitk:4005
+msgid "Strictly sort by date"
+msgstr "Sắp xếp chặt chẽ theo ngày"
+
+#: gitk:4006
+msgid "Mark branch sides"
+msgstr "Đánh dấu các cạnh nhánh"
+
+#: gitk:4007
+msgid "Limit to first parent"
+msgstr "Giới hạn thành cha mẹ đầu tiên"
+
+#: gitk:4008
+msgid "Simple history"
+msgstr "Lịch sử dạng đơn giản"
+
+#: gitk:4009
+msgid "Additional arguments to git log:"
+msgstr "Đối số bổ xung cho lệnh git log:"
+
+#: gitk:4010
+msgid "Enter files and directories to include, one per line:"
+msgstr "Nhập vào các tập tin và thư mục bao gồm, mỗi dòng một cái:"
+
+#: gitk:4011
+msgid "Command to generate more commits to include:"
+msgstr "Lệnh tạo ra nhiều lần chuyển giao hơn bao gồm:"
+
+#: gitk:4135
+msgid "Gitk: edit view"
+msgstr "Gitk: sửa cách trình bày"
+
+#: gitk:4143
+msgid "-- criteria for selecting revisions"
+msgstr "-- tiêu chuẩn chọn điểm xét duyệt"
+
+#: gitk:4148
+msgid "View Name"
+msgstr "Tên cách trình bày"
+
+#: gitk:4223
+msgid "Apply (F5)"
+msgstr "Áp dụng (F5)"
+
+#: gitk:4261
+msgid "Error in commit selection arguments:"
+msgstr "Lỗi trong các đối số chọn chuyển giao:"
+
+#: gitk:4314 gitk:4366 gitk:4827 gitk:4841 gitk:6107 gitk:12184 gitk:12185
+msgid "None"
+msgstr "Không"
+
+#: gitk:4924 gitk:4929
+msgid "Descendant"
+msgstr "Con cháu"
+
+#: gitk:4925
+msgid "Not descendant"
+msgstr "Không có con cháu"
+
+#: gitk:4932 gitk:4937
+msgid "Ancestor"
+msgstr "Tổ tiên chung"
+
+#: gitk:4933
+msgid "Not ancestor"
+msgstr "Không có chung tổ tiên"
+
+#: gitk:5223
+msgid "Local changes checked in to index but not committed"
+msgstr ""
+"Có thay đổi nội bộ đã được đưa vào bảng mục lục, nhưng chưa được chuyển giao"
+
+#: gitk:5259
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Có thay đổi nội bộ, nhưng chưa được đưa vào bảng mục lục"
+
+#: gitk:7032
+msgid "and many more"
+msgstr "và nhiều nữa"
+
+#: gitk:7035
+msgid "many"
+msgstr "nhiều"
+
+#: gitk:7222
+msgid "Tags:"
+msgstr "Thẻ:"
+
+#: gitk:7239 gitk:7245 gitk:8708
+msgid "Parent"
+msgstr "Cha"
+
+#: gitk:7250
+msgid "Child"
+msgstr "Con"
+
+#: gitk:7259
+msgid "Branch"
+msgstr "Nhánh"
+
+#: gitk:7262
+msgid "Follows"
+msgstr "Đứng sau"
+
+#: gitk:7265
+msgid "Precedes"
+msgstr "Đứng trước"
+
+# tcl-format
+#: gitk:7849
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Lỗi lấy diff: %s"
+
+#: gitk:8533
+msgid "Goto:"
+msgstr "Nhảy tới:"
+
+#: gitk:8554
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "Định danh SHA1 dạng ngắn %s là chưa đủ rõ ràng"
+
+#: gitk:8561
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "Không hiểu điểm xét duyệt %s"
+
+#: gitk:8571
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "Không hiểu định danh SHA1 %s"
+
+#: gitk:8573
+#, tcl-format
+msgid "Revision %s is not in the current view"
+msgstr "Điểm %s không ở trong phần hiển thị hiện tại"
+
+#: gitk:8715 gitk:8730
+msgid "Date"
+msgstr "Ngày"
+
+#: gitk:8718
+msgid "Children"
+msgstr "Con cháu"
+
+#: gitk:8781
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Đặt lại nhánh %s tại đây"
+
+#: gitk:8783
+msgid "Detached head: can't reset"
+msgstr "Head đã bị tách rời: không thể đặt lại"
+
+#: gitk:8888 gitk:8894
+msgid "Skipping merge commit "
+msgstr "Bỏ qua lần chuyển giao hòa trộn "
+
+#: gitk:8903 gitk:8908
+msgid "Error getting patch ID for "
+msgstr "Gặp lỗi khi lấy ID miếng vá cho "
+
+#: gitk:8904 gitk:8909
+msgid " - stopping\n"
+msgstr " - dừng\n"
+
+#: gitk:8914 gitk:8917 gitk:8925 gitk:8939 gitk:8948
+msgid "Commit "
+msgstr "Commit "
+
+#: gitk:8918
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+" là cùng một miếng vá với\n"
+"       "
+
+#: gitk:8926
+msgid ""
+" differs from\n"
+"       "
+msgstr ""
+" khác biệt từ\n"
+"       "
+
+#: gitk:8928
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr ""
+"Khác biệt của lần chuyển giao (commit):\n"
+"\n"
+
+#: gitk:8940 gitk:8949
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr " có %s con - dừng\n"
+
+#: gitk:8968
+#, tcl-format
+msgid "Error writing commit to file: %s"
+msgstr "Gặp lỗi trong quá trình ghi lần chuyển giao vào tập tin: %s"
+
+#: gitk:8974
+#, tcl-format
+msgid "Error diffing commits: %s"
+msgstr "Gặp lỗi khi so sánh sự khác biệt giữa các lần chuyển giao: %s"
+
+#: gitk:9020
+msgid "Top"
+msgstr "Đỉnh"
+
+#: gitk:9021
+msgid "From"
+msgstr "Từ"
+
+#: gitk:9026
+msgid "To"
+msgstr "Đến"
+
+#: gitk:9050
+msgid "Generate patch"
+msgstr "Tạo miếng vá"
+
+#: gitk:9052
+msgid "From:"
+msgstr "Từ:"
+
+#: gitk:9061
+msgid "To:"
+msgstr "Đến:"
+
+#: gitk:9070
+msgid "Reverse"
+msgstr "Đảo ngược"
+
+#: gitk:9072 gitk:9268
+msgid "Output file:"
+msgstr "Tập tin kết xuất:"
+
+#: gitk:9078
+msgid "Generate"
+msgstr "Tạo"
+
+#: gitk:9116
+msgid "Error creating patch:"
+msgstr "Gặp lỗi khi tạo miếng vá:"
+
+#: gitk:9139 gitk:9256 gitk:9313
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:9148
+msgid "Tag name:"
+msgstr "Tên thẻ:"
+
+#: gitk:9151
+msgid "Tag message is optional"
+msgstr "Ghi chú thẻ chỉ là tùy chọn"
+
+#: gitk:9153
+msgid "Tag message:"
+msgstr "Ghi chú cho thẻ:"
+
+#: gitk:9157 gitk:9322
+msgid "Create"
+msgstr "Tạo"
+
+#: gitk:9175
+msgid "No tag name specified"
+msgstr "Chưa chỉ ra tên của thẻ"
+
+#: gitk:9179
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "Thẻ “%s” đã có sẵn rồi"
+
+#: gitk:9189
+msgid "Error creating tag:"
+msgstr "Gặp lỗi khi tạo thẻ:"
+
+#: gitk:9265
+msgid "Command:"
+msgstr "Lệnh:"
+
+#: gitk:9273
+msgid "Write"
+msgstr "Ghi"
+
+#: gitk:9291
+msgid "Error writing commit:"
+msgstr "Gặp lỗi trong quá trình ghi chuyển giao:"
+
+#: gitk:9318
+msgid "Name:"
+msgstr "Tên:"
+
+#: gitk:9341
+msgid "Please specify a name for the new branch"
+msgstr "Vui lòng chỉ định tên cho nhánh mới"
+
+#: gitk:9346
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "Nhánh “%s” đã có từ trước rồi. Ghi đè?"
+
+#: gitk:9413
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr ""
+"Lần chuyển giao %s đã sẵn được bao gồm trong nhánh %s -- bạn có thực sự muốn "
+"áp dụng lại nó không?"
+
+#: gitk:9418
+msgid "Cherry-picking"
+msgstr "Đang cherry-pick"
+
+#: gitk:9427
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"Cherry-pick gặp lỗi bởi vì các thay đổi nội bộ tập tin “%s”.\n"
+"Xin hãy chuyển giao, reset hay stash các thay đổi của bạn sau đó thử lại."
+
+#: gitk:9433
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"Cherry-pick gặp lỗi bởi vì xung đột trong hòa trộn.\n"
+"Bạn có muốn chạy lệnh “git citool” để giải quyết vấn đề này không?"
+
+#: gitk:9449 gitk:9507
+msgid "No changes committed"
+msgstr "Không có thay đổi nào cần chuyển giao"
+
+#: gitk:9476
+#, tcl-format
+msgid "Commit %s is not included in branch %s -- really revert it?"
+msgstr ""
+"Lần chuyển giao %s không được bao gồm trong nhánh %s -- bạn có thực sự muốn "
+"“revert” nó không?"
+
+#: gitk:9481
+msgid "Reverting"
+msgstr "Đang hoàn tác"
+
+#: gitk:9489
+#, tcl-format
+msgid ""
+"Revert failed because of local changes to the following files:%s Please "
+"commit, reset or stash  your changes and try again."
+msgstr ""
+"Revert gặp lỗi bởi vì tập tin sau đã được thay đổi nội bộ:%s\n"
+"Xin hãy chạy lệnh “commit”, “reset” hoặc “stash” rồi thử lại."
+
+#: gitk:9493
+msgid ""
+"Revert failed because of merge conflict.\n"
+" Do you wish to run git citool to resolve it?"
+msgstr ""
+"Revert gặp lỗi bởi vì xung đột hòa trộn.\n"
+" Bạn có muốn chạy lệnh “git citool” để phân giải nó không?"
+
+#: gitk:9536
+msgid "Confirm reset"
+msgstr "Xác nhật đặt lại"
+
+#: gitk:9538
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Đặt lại nhánh “%s” thành “%s”?"
+
+#: gitk:9540
+msgid "Reset type:"
+msgstr "Kiểu đặt lại:"
+
+#: gitk:9543
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Mềm: Không động đến thư mục làm việc và bảng mục lục"
+
+#: gitk:9546
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr ""
+"Pha trộn: Không động chạm đến thư mục làm việc nhưng đặt lại bảng mục lục"
+
+#: gitk:9549
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Hard: Đặt lại cây làm việc và mục lục\n"
+"(hủy bỏ MỌI thay đổi nội bộ)"
+
+#: gitk:9566
+msgid "Resetting"
+msgstr "Đang đặt lại"
+
+#: gitk:9626
+msgid "Checking out"
+msgstr "Đang checkout"
+
+#: gitk:9679
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Không thể xóa nhánh hiện tại đang được lấy ra"
+
+#: gitk:9685
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Các lần chuyển giao trên nhánh %s không ở trên nhánh khác.\n"
+"Thực sự muốn xóa nhánh %s?"
+
+#: gitk:9716
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Thẻ và Đầu: %s"
+
+#: gitk:9731
+msgid "Filter"
+msgstr "Bộ lọc"
+
+#: gitk:10027
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Gặp lỗi khi đọc thông tin hình học lần chuyển giao; thông tin nhánh và thẻ "
+"trước/sau sẽ không hoàn thiện."
+
+#: gitk:11004
+msgid "Tag"
+msgstr "Thẻ"
+
+#: gitk:11008
+msgid "Id"
+msgstr "Id"
+
+#: gitk:11091
+msgid "Gitk font chooser"
+msgstr "Hộp thoại chọn phông Gitk"
+
+#: gitk:11108
+msgid "B"
+msgstr "B"
+
+#: gitk:11111
+msgid "I"
+msgstr "I"
+
+#: gitk:11229
+msgid "Commit list display options"
+msgstr "Các tùy chọn về hiển thị danh sách lần chuyển giao"
+
+#: gitk:11232
+msgid "Maximum graph width (lines)"
+msgstr "Độ rộng biểu đồ tối đa (dòng)"
+
+#: gitk:11235
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Độ rộng biểu đồ tối đa (% của bảng)"
+
+#: gitk:11238
+msgid "Show local changes"
+msgstr "Hiển thị các thay đổi nội bộ"
+
+#: gitk:11241
+msgid "Auto-select SHA1 (length)"
+msgstr "Tự chọn SHA1 (độ dài)"
+
+#: gitk:11245
+msgid "Hide remote refs"
+msgstr "Ẩn tham chiếu đến máy chủ"
+
+#: gitk:11249
+msgid "Diff display options"
+msgstr "Các tùy chọn trình bày các khác biệt"
+
+#: gitk:11251
+msgid "Tab spacing"
+msgstr "Khoảng cách tab"
+
+#: gitk:11254
+msgid "Display nearby tags/heads"
+msgstr "Hiển thị các thẻ/đầu xung quanh"
+
+#: gitk:11257
+msgid "Maximum # tags/heads to show"
+msgstr "Số lượng thẻ/đầu tối đa sẽ hiển thị"
+
+#: gitk:11260
+msgid "Limit diffs to listed paths"
+msgstr "Giới hạn các khác biệt cho đường dẫn đã liệt kê"
+
+#: gitk:11263
+msgid "Support per-file encodings"
+msgstr "Hỗ trợ mã hóa mỗi-dòng"
+
+#: gitk:11269 gitk:11416
+msgid "External diff tool"
+msgstr "Công cụ so sánh từ bên ngoài"
+
+#: gitk:11270
+msgid "Choose..."
+msgstr "Chọn..."
+
+#: gitk:11275
+msgid "General options"
+msgstr "Các tùy chọn chung"
+
+#: gitk:11278
+msgid "Use themed widgets"
+msgstr "Dùng các widget chủ đề"
+
+#: gitk:11280
+msgid "(change requires restart)"
+msgstr "(để thay đổi cần khởi động lại)"
+
+#: gitk:11282
+msgid "(currently unavailable)"
+msgstr "(hiện tại không sẵn sàng)"
+
+#: gitk:11293
+msgid "Colors: press to choose"
+msgstr "Màu sắc: bấm vào nút phía dưới để chọn màu"
+
+#: gitk:11296
+msgid "Interface"
+msgstr "Giao diện"
+
+#: gitk:11297
+msgid "interface"
+msgstr "giao diện"
+
+#: gitk:11300
+msgid "Background"
+msgstr "Nền"
+
+#: gitk:11301 gitk:11331
+msgid "background"
+msgstr "nền"
+
+#: gitk:11304
+msgid "Foreground"
+msgstr "Tiền cảnh"
+
+#: gitk:11305
+msgid "foreground"
+msgstr "tiền cảnh"
+
+#: gitk:11308
+msgid "Diff: old lines"
+msgstr "So sánh: dòng cũ"
+
+#: gitk:11309
+msgid "diff old lines"
+msgstr "diff dòng cũ"
+
+#: gitk:11313
+msgid "Diff: new lines"
+msgstr "So sánh: dòng mới"
+
+#: gitk:11314
+msgid "diff new lines"
+msgstr "màu dòng mới"
+
+#: gitk:11318
+msgid "Diff: hunk header"
+msgstr "So sánh: phần đầu của đoạn"
+
+#: gitk:11320
+msgid "diff hunk header"
+msgstr "màu của phần đầu của đoạn khi so sánh"
+
+#: gitk:11324
+msgid "Marked line bg"
+msgstr "Nền dòng đánh dấu"
+
+#: gitk:11326
+msgid "marked line background"
+msgstr "nền dòng được đánh dấu"
+
+#: gitk:11330
+msgid "Select bg"
+msgstr "Màu nền"
+
+#: gitk:11339
+msgid "Fonts: press to choose"
+msgstr "Phông chữ: bấm vào các nút ở dưới để chọn"
+
+#: gitk:11341
+msgid "Main font"
+msgstr "Phông chữ chính"
+
+#: gitk:11342
+msgid "Diff display font"
+msgstr "Phông chữ dùng khi so sánh"
+
+#: gitk:11343
+msgid "User interface font"
+msgstr "Phông chữ giao diện"
+
+#: gitk:11365
+msgid "Gitk preferences"
+msgstr "Cá nhân hóa các cài đặt cho Gitk"
+
+#: gitk:11374
+msgid "General"
+msgstr "Chung"
+
+#: gitk:11375
+msgid "Colors"
+msgstr "Màu sắc"
+
+#: gitk:11376
+msgid "Fonts"
+msgstr "Phông chữ"
+
+#: gitk:11426
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: chọn màu cho %s"
+
+#: gitk:12080
+msgid "Cannot find a git repository here."
+msgstr "Không thể tìm thấy kho git ở đây."
+
+#: gitk:12127
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Đối số “%s” chưa rõ ràng: vừa là điểm xét duyệt vừa là tên tập tin"
+
+#: gitk:12139
+msgid "Bad arguments to gitk:"
+msgstr "Đối số không hợp lệ cho gitk:"
+
+#: gitk:12242
+msgid "Command line"
+msgstr "Dòng lệnh"
index 8b0e87436b687ce26e3e4987129a2fc171069280..ff07012726ea28daa2551966d0555d2e8efa2375 100644 (file)
@@ -7,6 +7,20 @@
 static char *configured_signing_key;
 static const char *gpg_program = "gpg";
 
+void signature_check_clear(struct signature_check *sigc)
+{
+       free(sigc->payload);
+       free(sigc->gpg_output);
+       free(sigc->gpg_status);
+       free(sigc->signer);
+       free(sigc->key);
+       sigc->payload = NULL;
+       sigc->gpg_output = NULL;
+       sigc->gpg_status = NULL;
+       sigc->signer = NULL;
+       sigc->key = NULL;
+}
+
 void set_signing_key(const char *key)
 {
        free(configured_signing_key);
index a85cb5bc97cdd61000b4c48c54faa656aa3cfaca..37c23daff010b0de18fa12ff6a6167f45ff41ffc 100644 (file)
@@ -2,6 +2,7 @@
 #define GPG_INTERFACE_H
 
 struct signature_check {
+       char *payload;
        char *gpg_output;
        char *gpg_status;
        char result; /* 0 (not checked),
@@ -13,6 +14,7 @@ struct signature_check {
        char *key;
 };
 
+extern void signature_check_clear(struct signature_check *sigc);
 extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
 extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output, struct strbuf *gpg_status);
 extern int git_gpg_config(const char *, const char *, void *);
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 c5c95e85ea305540b7863512617adca35b858534..6c3cc1725a9461876c7db11b9818eb553afb2fc2 100644 (file)
@@ -763,15 +763,13 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
 
        if (tag_closed && ctx->cdata) {
                if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
-                       lock->owner = xmalloc(strlen(ctx->cdata) + 1);
-                       strcpy(lock->owner, ctx->cdata);
+                       lock->owner = xstrdup(ctx->cdata);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {
                        const char *arg;
                        if (skip_prefix(ctx->cdata, "Second-", &arg))
                                lock->timeout = strtol(arg, NULL, 10);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
-                       lock->token = xmalloc(strlen(ctx->cdata) + 1);
-                       strcpy(lock->token, ctx->cdata);
+                       lock->token = xstrdup(ctx->cdata);
 
                        git_SHA1_Init(&sha_ctx);
                        git_SHA1_Update(&sha_ctx, lock->token, strlen(lock->token));
@@ -852,8 +850,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
        struct xml_ctx ctx;
        char *escaped;
 
-       url = xmalloc(strlen(repo->url) + strlen(path) + 1);
-       sprintf(url, "%s%s", repo->url, path);
+       url = xstrfmt("%s%s", repo->url, path);
 
        /* Make sure leading directories exist for the remote ref */
        ep = strchr(url + strlen(repo->url) + 1, '/');
@@ -1125,7 +1122,7 @@ static void remote_ls(const char *path, int flags,
                      void (*userFunc)(struct remote_ls_ctx *ls),
                      void *userData)
 {
-       char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
+       char *url = xstrfmt("%s%s", repo->url, path);
        struct active_request_slot *slot;
        struct slot_results results;
        struct strbuf in_buffer = STRBUF_INIT;
@@ -1141,8 +1138,6 @@ static void remote_ls(const char *path, int flags,
        ls.userData = userData;
        ls.userFunc = userFunc;
 
-       sprintf(url, "%s%s", repo->url, path);
-
        strbuf_addf(&out_buffer.buf, PROPFIND_ALL_REQUEST);
 
        dav_headers = curl_slist_append(dav_headers, "Depth: 1");
@@ -1544,10 +1539,9 @@ static void update_remote_info_refs(struct remote_lock *lock)
 
 static int remote_exists(const char *path)
 {
-       char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
+       char *url = xstrfmt("%s%s", repo->url, path);
        int ret;
 
-       sprintf(url, "%s%s", repo->url, path);
 
        switch (http_get_strbuf(url, NULL, NULL)) {
        case HTTP_OK:
@@ -1567,13 +1561,10 @@ static int remote_exists(const char *path)
 
 static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
 {
-       char *url;
+       char *url = xstrfmt("%s%s", repo->url, path);
        struct strbuf buffer = STRBUF_INIT;
        const char *name;
 
-       url = xmalloc(strlen(repo->url) + strlen(path) + 1);
-       sprintf(url, "%s%s", repo->url, path);
-
        if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK)
                die("Couldn't get %s for remote symref\n%s", url,
                    curl_errorstr);
@@ -1682,8 +1673,7 @@ static int delete_remote_branch(const char *pattern, int force)
        fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name);
        if (dry_run)
                return 0;
-       url = xmalloc(strlen(repo->url) + strlen(remote_ref->name) + 1);
-       sprintf(url, "%s%s", repo->url, remote_ref->name);
+       url = xstrfmt("%s%s", repo->url, remote_ref->name);
        slot = get_active_slot();
        slot->results = &results;
        curl_setup_http_get(slot->curl, url, DAV_DELETE);
index 1516c5eb290746bad4551515fda7930d2f55d32b..dbddfaa1773265a2917d4715158f16845d7139e5 100644 (file)
@@ -341,8 +341,7 @@ static void fetch_alternates(struct walker *walker, const char *base)
        if (walker->get_verbosely)
                fprintf(stderr, "Getting alternates list for %s\n", base);
 
-       url = xmalloc(strlen(base) + 31);
-       sprintf(url, "%s/objects/info/http-alternates", base);
+       url = xstrfmt("%s/objects/info/http-alternates", base);
 
        /*
         * Use a callback to process the result, since another request
@@ -566,8 +565,7 @@ struct walker *get_http_walker(const char *url)
        struct walker *walker = xmalloc(sizeof(struct walker));
 
        data->alt = xmalloc(sizeof(*data->alt));
-       data->alt->base = xmalloc(strlen(url) + 1);
-       strcpy(data->alt->base, url);
+       data->alt->base = xstrdup(url);
        for (s = data->alt->base + strlen(data->alt->base) - 1; *s == '/'; --s)
                *s = 0;
 
diff --git a/http.c b/http.c
index f04621e61d157478f4100dc710118e1bd8634c70..c8cd50dd0c2b2213a86957cd4b24c03c0d85717d 100644 (file)
--- a/http.c
+++ b/http.c
@@ -927,7 +927,7 @@ static int extract_param(const char *raw, const char *name,
                return -1;
        raw++;
 
-       while (*raw && !isspace(*raw))
+       while (*raw && !isspace(*raw) && *raw != ';')
                strbuf_addch(out, *raw++);
        return 0;
 }
@@ -971,7 +971,7 @@ static void extract_content_type(struct strbuf *raw, struct strbuf *type,
 
        strbuf_reset(charset);
        while (*p) {
-               while (isspace(*p))
+               while (isspace(*p) || *p == ';')
                        p++;
                if (!extract_param(p, "charset", charset))
                        return;
index 150010105806291180d9e6bdfcf1939fb242f2ac..afcc98db930a36358283ff199a428b6c5bbbaf8f 100644 (file)
@@ -1174,9 +1174,7 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
                         */
                        add_line_range(rev, parents[i], cand[i]);
                        clear_commit_line_range(rev, commit);
-                       commit->parents = xmalloc(sizeof(struct commit_list));
-                       commit->parents->item = parents[i];
-                       commit->parents->next = NULL;
+                       commit_list_append(parents[i], &commit->parents);
                        free(parents);
                        free(cand);
                        free_diffqueues(nparents, diffqueues);
index 8fbcb6a98aae85b48c6f8de29c6f95af58823411..b7066143494bcfa3e629dd718daf8959af6c4b86 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)
 {
@@ -252,25 +251,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 cf2f86c86632c6df8862fc065299e6cc35aa4829..f87b7e891adc5dfb429bc0ae35227e19f335b90f 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;
        }
 }
@@ -376,7 +377,7 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
        struct strbuf gpg_output = STRBUF_INIT;
        int status;
 
-       if (parse_signed_commit(commit->object.sha1, &payload, &signature) <= 0)
+       if (parse_signed_commit(commit, &payload, &signature) <= 0)
                goto out;
 
        status = verify_signed_buffer(payload.buf, payload.len,
@@ -446,16 +447,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);
@@ -588,7 +590,7 @@ void show_log(struct rev_info *opt)
                show_mergetag(opt, commit);
        }
 
-       if (!commit->buffer)
+       if (!get_cached_commit_buffer(commit, NULL))
                return;
 
        if (opt->show_notes) {
index e80b4af354f87caa7a5130660e9c9c530246b8be..1ce0954a3e5915a6bcc30448c9d99fdaba32bcab 100644 (file)
@@ -140,17 +140,12 @@ static void match_trees(const unsigned char *hash1,
                        goto next;
                score = score_trees(elem, hash2);
                if (*best_score < score) {
-                       char *newpath;
-                       newpath = xmalloc(strlen(base) + strlen(path) + 1);
-                       sprintf(newpath, "%s%s", base, path);
                        free(*best_match);
-                       *best_match = newpath;
+                       *best_match = xstrfmt("%s%s", base, path);
                        *best_score = score;
                }
                if (recurse_limit) {
-                       char *newbase;
-                       newbase = xmalloc(strlen(base) + strlen(path) + 2);
-                       sprintf(newbase, "%s%s/", base, path);
+                       char *newbase = xstrfmt("%s%s/", base, path);
                        match_trees(elem, hash2, best_score, best_match,
                                    newbase, recurse_limit - 1);
                        free(newbase);
index f8480018178c7eb8b88dcd3e1f166dc953bfbe11..8e44d7e5f3d72ee71580dc98d5e7d13c4d56779f 100644 (file)
@@ -40,7 +40,7 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two,
 
 static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
 {
-       struct commit *commit = xcalloc(1, sizeof(struct commit));
+       struct commit *commit = alloc_commit_node();
        struct merge_remote_desc *desc = xmalloc(sizeof(*desc));
 
        desc->name = comment;
@@ -190,9 +190,11 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
                        printf(_("(bad commit)\n"));
                else {
                        const char *title;
-                       int len = find_commit_subject(commit->buffer, &title);
+                       const char *msg = get_commit_buffer(commit, NULL);
+                       int len = find_commit_subject(msg, &title);
                        if (len)
                                printf("%.*s\n", len, title);
+                       unuse_commit_buffer(commit, msg);
                }
        }
 }
@@ -265,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);
@@ -601,25 +601,36 @@ static int remove_file(struct merge_options *o, int clean,
        return 0;
 }
 
+/* add a string to a strbuf, but converting "/" to "_" */
+static void add_flattened_path(struct strbuf *out, const char *s)
+{
+       size_t i = out->len;
+       strbuf_addstr(out, s);
+       for (; i < out->len; i++)
+               if (out->buf[i] == '/')
+                       out->buf[i] = '_';
+}
+
 static char *unique_path(struct merge_options *o, const char *path, const char *branch)
 {
-       char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
+       struct strbuf newpath = STRBUF_INIT;
        int suffix = 0;
        struct stat st;
-       char *p = newpath + strlen(path);
-       strcpy(newpath, path);
-       *(p++) = '~';
-       strcpy(p, branch);
-       for (; *p; ++p)
-               if ('/' == *p)
-                       *p = '_';
-       while (string_list_has_string(&o->current_file_set, newpath) ||
-              string_list_has_string(&o->current_directory_set, newpath) ||
-              lstat(newpath, &st) == 0)
-               sprintf(p, "_%d", suffix++);
-
-       string_list_insert(&o->current_file_set, newpath);
-       return newpath;
+       size_t base_len;
+
+       strbuf_addf(&newpath, "%s~", path);
+       add_flattened_path(&newpath, branch);
+
+       base_len = newpath.len;
+       while (string_list_has_string(&o->current_file_set, newpath.buf) ||
+              string_list_has_string(&o->current_directory_set, newpath.buf) ||
+              lstat(newpath.buf, &st) == 0) {
+               strbuf_setlen(&newpath, base_len);
+               strbuf_addf(&newpath, "_%d", suffix++);
+       }
+
+       string_list_insert(&o->current_file_set, newpath.buf);
+       return strbuf_detach(&newpath, NULL);
 }
 
 static int dir_in_way(const char *path, int check_working_copy)
@@ -969,14 +980,10 @@ merge_file_special_markers(struct merge_options *o,
        char *side2 = NULL;
        struct merge_file_info mfi;
 
-       if (filename1) {
-               side1 = xmalloc(strlen(branch1) + strlen(filename1) + 2);
-               sprintf(side1, "%s:%s", branch1, filename1);
-       }
-       if (filename2) {
-               side2 = xmalloc(strlen(branch2) + strlen(filename2) + 2);
-               sprintf(side2, "%s:%s", branch2, filename2);
-       }
+       if (filename1)
+               side1 = xstrfmt("%s:%s", branch1, filename1);
+       if (filename2)
+               side2 = xstrfmt("%s:%s", branch2, filename2);
 
        mfi = merge_file_1(o, one, a, b,
                           side1 ? side1 : branch1, side2 ? side2 : branch2);
@@ -1992,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);
@@ -2009,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;
diff --git a/merge.c b/merge.c
index 70f1000fcbef5cb04e5b25791b482b5f40fab204..74ced7f70b5beec045c134b5719c926fc791f1e2 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -18,39 +18,23 @@ int try_merge_command(const char *strategy, size_t xopts_nr,
                      const char **xopts, struct commit_list *common,
                      const char *head_arg, struct commit_list *remotes)
 {
-       const char **args;
-       int i = 0, x = 0, ret;
+       struct argv_array args = ARGV_ARRAY_INIT;
+       int i, ret;
        struct commit_list *j;
-       struct strbuf buf = STRBUF_INIT;
 
-       args = xmalloc((4 + xopts_nr + commit_list_count(common) +
-                       commit_list_count(remotes)) * sizeof(char *));
-       strbuf_addf(&buf, "merge-%s", strategy);
-       args[i++] = buf.buf;
-       for (x = 0; x < xopts_nr; x++) {
-               char *s = xmalloc(strlen(xopts[x])+2+1);
-               strcpy(s, "--");
-               strcpy(s+2, xopts[x]);
-               args[i++] = s;
-       }
-       for (j = common; j; j = j->next)
-               args[i++] = xstrdup(merge_argument(j->item));
-       args[i++] = "--";
-       args[i++] = head_arg;
-       for (j = remotes; j; j = j->next)
-               args[i++] = xstrdup(merge_argument(j->item));
-       args[i] = NULL;
-       ret = run_command_v_opt(args, RUN_GIT_CMD);
-       strbuf_release(&buf);
-       i = 1;
-       for (x = 0; x < xopts_nr; x++)
-               free((void *)args[i++]);
+       argv_array_pushf(&args, "merge-%s", strategy);
+       for (i = 0; i < xopts_nr; i++)
+               argv_array_pushf(&args, "--%s", xopts[i]);
        for (j = common; j; j = j->next)
-               free((void *)args[i++]);
-       i += 2;
+               argv_array_push(&args, merge_argument(j->item));
+       argv_array_push(&args, "--");
+       argv_array_push(&args, head_arg);
        for (j = remotes; j; j = j->next)
-               free((void *)args[i++]);
-       free(args);
+               argv_array_push(&args, merge_argument(j->item));
+
+       ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
+       argv_array_clear(&args);
+
        discard_cache();
        if (read_cache() < 0)
                die(_("failed to read the cache"));
@@ -66,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));
@@ -105,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 97444d02010e13de1eab9abe6f1fc71287658faf..49fd508317ebbde5a76f6af64318920ae237c8fd 100644 (file)
@@ -179,7 +179,7 @@ static int same_name(const struct cache_entry *ce, const char *name, int namelen
         * Always do exact compare, even if we want a case-ignoring comparison;
         * we do the quick exact one first, because it will be the common case.
         */
-       if (len == namelen && !cache_name_compare(name, namelen, ce->name, len))
+       if (len == namelen && !memcmp(name, ce->name, len))
                return 1;
 
        if (!icase)
index 97dfd63c9bf564ac052addc4a3f62a325bbc1303..c4e9bb7f6c0bde97c7c6d34094baa5ef7e318157 100644 (file)
@@ -48,7 +48,6 @@ int notes_cache_write(struct notes_cache *c)
 {
        unsigned char tree_sha1[20];
        unsigned char commit_sha1[20];
-       struct strbuf msg = STRBUF_INIT;
 
        if (!c || !c->tree.initialized || !c->tree.ref || !*c->tree.ref)
                return -1;
@@ -57,9 +56,8 @@ int notes_cache_write(struct notes_cache *c)
 
        if (write_notes_tree(&c->tree, tree_sha1))
                return -1;
-       strbuf_attach(&msg, c->validity,
-                     strlen(c->validity), strlen(c->validity) + 1);
-       if (commit_tree(&msg, tree_sha1, NULL, commit_sha1, NULL, NULL) < 0)
+       if (commit_tree(c->validity, strlen(c->validity), tree_sha1, NULL,
+                       commit_sha1, NULL, NULL) < 0)
                return -1;
        if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
                       0, UPDATE_REFS_QUIET_ON_ERR) < 0)
index 94a1a8ae466733b207ae5030f8a8894d66535f91..fd5fae255d2760d6b7895e95cee627b5e28f776e 100644 (file)
@@ -644,7 +644,8 @@ int notes_merge(struct notes_merge_options *o,
                struct commit_list *parents = NULL;
                commit_list_insert(remote, &parents); /* LIFO order */
                commit_list_insert(local, &parents);
-               create_notes_commit(local_tree, parents, &o->commit_msg,
+               create_notes_commit(local_tree, parents,
+                                   o->commit_msg.buf, o->commit_msg.len,
                                    result_sha1);
        }
 
@@ -671,8 +672,8 @@ int notes_merge_commit(struct notes_merge_options *o,
        DIR *dir;
        struct dirent *e;
        struct strbuf path = STRBUF_INIT;
-       char *msg = strstr(partial_commit->buffer, "\n\n");
-       struct strbuf sb_msg = STRBUF_INIT;
+       const char *buffer = get_commit_buffer(partial_commit, NULL);
+       const char *msg = strstr(buffer, "\n\n");
        int baselen;
 
        strbuf_addstr(&path, git_path(NOTES_MERGE_WORKTREE));
@@ -719,9 +720,9 @@ int notes_merge_commit(struct notes_merge_options *o,
                strbuf_setlen(&path, baselen);
        }
 
-       strbuf_attach(&sb_msg, msg, strlen(msg), strlen(msg) + 1);
-       create_notes_commit(partial_tree, partial_commit->parents, &sb_msg,
-                           result_sha1);
+       create_notes_commit(partial_tree, partial_commit->parents,
+                           msg, strlen(msg), result_sha1);
+       unuse_commit_buffer(partial_commit, buffer);
        if (o->verbosity >= 4)
                printf("Finalized notes merge commit: %s\n",
                        sha1_to_hex(result_sha1));
index a0b1d7be98253368921a6e95653ceb36dbda28b2..b64dc1b0219928ac149894c99a63dcc9fd419668 100644 (file)
@@ -4,7 +4,8 @@
 #include "notes-utils.h"
 
 void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
-                        const struct strbuf *msg, unsigned char *result_sha1)
+                        const char *msg, size_t msg_len,
+                        unsigned char *result_sha1)
 {
        unsigned char tree_sha1[20];
 
@@ -25,7 +26,7 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
                /* else: t->ref points to nothing, assume root/orphan commit */
        }
 
-       if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
+       if (commit_tree(msg, msg_len, tree_sha1, parents, result_sha1, NULL, NULL))
                die("Failed to commit notes tree to database");
 }
 
@@ -46,7 +47,7 @@ void commit_notes(struct notes_tree *t, const char *msg)
        if (buf.buf[buf.len - 1] != '\n')
                strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
 
-       create_notes_commit(t, NULL, &buf, commit_sha1);
+       create_notes_commit(t, NULL, buf.buf, buf.len, commit_sha1);
        strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
        update_ref(buf.buf, t->ref, commit_sha1, NULL, 0,
                   UPDATE_REFS_DIE_ON_ERR);
index 564e30cccd550f9acbd493f43c940bab1abba427..890ddb33e13ad6b9d43887216acbe5430ed534cc 100644 (file)
@@ -15,7 +15,7 @@
  * The resulting commit SHA1 is stored in result_sha1.
  */
 void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
-                        const struct strbuf *msg, unsigned char *result_sha1);
+                        const char *msg, size_t msg_len, unsigned char *result_sha1);
 
 void commit_notes(struct notes_tree *t, const char *msg);
 
index 57a0890a87b66ea493337409606e57fd44d29f90..9c31e9a5e0e0d913044501089e09037e47894f03 100644 (file)
--- a/object.c
+++ b/object.c
@@ -197,8 +197,8 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
                if (commit) {
                        if (parse_commit_buffer(commit, buffer, size))
                                return NULL;
-                       if (!commit->buffer) {
-                               commit->buffer = buffer;
+                       if (!get_cached_commit_buffer(commit, NULL)) {
+                               set_commit_buffer(commit, buffer, size);
                                *eaten_p = 1;
                        }
                        obj = &commit->object;
index 968ee25eae13f82fe4f93254d77660456732a5c6..c1fe3a3ef9cba0c155d02475e24315784bd27ae4 100644 (file)
@@ -37,9 +37,8 @@ static void *preload_thread(void *_data)
        struct thread_data *p = _data;
        struct index_state *index = p->index;
        struct cache_entry **cep = index->cache + p->offset;
-       struct cache_def cache;
+       struct cache_def cache = CACHE_DEF_INIT;
 
-       memset(&cache, 0, sizeof(cache));
        nr = p->nr;
        if (nr + p->offset > index->cache_nr)
                nr = index->cache_nr - p->offset;
@@ -64,6 +63,7 @@ static void *preload_thread(void *_data)
                        continue;
                ce_mark_uptodate(ce);
        } while (--nr > 0);
+       cache_def_clear(&cache);
        return NULL;
 }
 
index f24752a85bc101c117d91b86a0526ca4c1eceee0..14357e233f3174963add310a29bb35c3de229121 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -273,7 +273,7 @@ static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
 
 enum rfc2047_type {
        RFC2047_SUBJECT,
-       RFC2047_ADDRESS,
+       RFC2047_ADDRESS
 };
 
 static int is_rfc2047_special(char ch, enum rfc2047_type type)
@@ -605,29 +605,16 @@ static char *replace_encoding_header(char *buf, const char *encoding)
        return strbuf_detach(&tmp, NULL);
 }
 
-char *logmsg_reencode(const struct commit *commit,
-                     char **commit_encoding,
-                     const char *output_encoding)
+const char *logmsg_reencode(const struct commit *commit,
+                           char **commit_encoding,
+                           const char *output_encoding)
 {
        static const char *utf8 = "UTF-8";
        const char *use_encoding;
        char *encoding;
-       char *msg = commit->buffer;
+       const char *msg = get_commit_buffer(commit, NULL);
        char *out;
 
-       if (!msg) {
-               enum object_type type;
-               unsigned long size;
-
-               msg = read_sha1_file(commit->object.sha1, &type, &size);
-               if (!msg)
-                       die("Cannot read commit object %s",
-                           sha1_to_hex(commit->object.sha1));
-               if (type != OBJ_COMMIT)
-                       die("Expected commit for '%s', got %s",
-                           sha1_to_hex(commit->object.sha1), typename(type));
-       }
-
        if (!output_encoding || !*output_encoding) {
                if (commit_encoding)
                        *commit_encoding =
@@ -651,12 +638,13 @@ char *logmsg_reencode(const struct commit *commit,
                 * Otherwise, we still want to munge the encoding header in the
                 * result, which will be done by modifying the buffer. If we
                 * are using a fresh copy, we can reuse it. But if we are using
-                * the cached copy from commit->buffer, we need to duplicate it
-                * to avoid munging commit->buffer.
+                * the cached copy from get_commit_buffer, we need to duplicate it
+                * to avoid munging the cached copy.
                 */
-               out = msg;
-               if (out == commit->buffer)
-                       out = xstrdup(out);
+               if (msg == get_cached_commit_buffer(commit, NULL))
+                       out = xstrdup(msg);
+               else
+                       out = (char *)msg;
        }
        else {
                /*
@@ -666,8 +654,8 @@ char *logmsg_reencode(const struct commit *commit,
                 * copy, we can free it.
                 */
                out = reencode_string(msg, output_encoding, use_encoding);
-               if (out && msg != commit->buffer)
-                       free(msg);
+               if (out)
+                       unuse_commit_buffer(commit, msg);
        }
 
        /*
@@ -686,12 +674,6 @@ char *logmsg_reencode(const struct commit *commit,
        return out ? out : msg;
 }
 
-void logmsg_free(char *msg, const struct commit *commit)
-{
-       if (msg != commit->buffer)
-               free(msg);
-}
-
 static int mailmap_name(const char **email, size_t *email_len,
                        const char **name, size_t *name_len)
 {
@@ -795,7 +777,7 @@ struct format_commit_context {
        struct signature_check signature_check;
        enum flush_type flush_type;
        enum trunc_type truncate;
-       char *message;
+       const char *message;
        char *commit_encoding;
        size_t width, indent1, indent2;
        int auto_color;
@@ -1266,6 +1248,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        if (c->signature_check.key)
                                strbuf_addstr(sb, c->signature_check.key);
                        break;
+               default:
+                       return 0;
                }
                return 2;
        }
@@ -1535,9 +1519,7 @@ void format_commit_message(const struct commit *commit,
        }
 
        free(context.commit_encoding);
-       logmsg_free(context.message, commit);
-       free(context.signature_check.gpg_output);
-       free(context.signature_check.signer);
+       unuse_commit_buffer(commit, context.message);
 }
 
 static void pp_header(struct pretty_print_context *pp,
@@ -1704,7 +1686,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
        unsigned long beginning_of_body;
        int indent = 4;
        const char *msg;
-       char *reencoded;
+       const char *reencoded;
        const char *encoding;
        int need_8bit_cte = pp->need_8bit_cte;
 
@@ -1771,7 +1753,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
        if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
 
-       logmsg_free(reencoded, commit);
+       unuse_commit_buffer(commit, reencoded);
 }
 
 void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
index 7f5645e74546e459efdb584dbf63e1fd75857317..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);
 }
@@ -422,18 +434,26 @@ int df_name_compare(const char *name1, int len1, int mode1,
        return c1 - c2;
 }
 
-int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2)
+int name_compare(const char *name1, size_t len1, const char *name2, size_t len2)
 {
-       int len = len1 < len2 ? len1 : len2;
-       int cmp;
-
-       cmp = memcmp(name1, name2, len);
+       size_t min_len = (len1 < len2) ? len1 : len2;
+       int cmp = memcmp(name1, name2, min_len);
        if (cmp)
                return cmp;
        if (len1 < len2)
                return -1;
        if (len1 > len2)
                return 1;
+       return 0;
+}
+
+int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2)
+{
+       int cmp;
+
+       cmp = name_compare(name1, len1, name2, len2);
+       if (cmp)
+               return cmp;
 
        if (stage1 < stage2)
                return -1;
@@ -442,11 +462,6 @@ int cache_name_stage_compare(const char *name1, int len1, int stage1, const char
        return 0;
 }
 
-int cache_name_compare(const char *name1, int len1, const char *name2, int len2)
-{
-       return cache_name_stage_compare(name1, len1, 0, name2, len2, 0);
-}
-
 static int index_name_stage_pos(const struct index_state *istate, const char *name, int namelen, int stage)
 {
        int first, last;
@@ -480,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;
@@ -504,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;
 }
 
@@ -518,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;
@@ -567,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;
@@ -580,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;
 }
 
@@ -673,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 */
@@ -936,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. */
@@ -999,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;
 }
 
@@ -1098,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;
 }
 
@@ -1179,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;
@@ -1331,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",
@@ -1365,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';
@@ -1439,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;
@@ -1456,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))
@@ -1477,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);
@@ -1531,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);
@@ -1540,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;
@@ -1553,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;
 }
 
@@ -1613,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;
 
@@ -1631,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;
 }
@@ -1702,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) {
@@ -1721,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);
@@ -1755,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);
@@ -1824,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;
@@ -1852,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)
@@ -1893,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);
@@ -1903,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);
@@ -1915,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 dc457742eade241ec6b0001580d1ce1a880fee31..061d233e4b6404c57ff8d62715758b900d02e051 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -7,21 +7,27 @@
 
 /*
  * 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: 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
+ * 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)
  */
 static unsigned char refname_disposition[256] = {
-       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,
+       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,
        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, 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
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 7, 7
 };
 
 /*
@@ -33,8 +39,9 @@ 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 ends with a "/".
- * - it ends with ".lock"
+ * - it has pattern-matching notation "*", "?", "[", anywhere, or
+ * - it ends with a "/", or
+ * - it ends with ".lock", or
  * - it contains a "\" (backslash)
  */
 static int check_refname_component(const char *refname, int flags)
@@ -46,17 +53,19 @@ static int check_refname_component(const char *refname, int flags)
                int ch = *cp & 255;
                unsigned char disp = refname_disposition[ch];
                switch (disp) {
-               case 1:
+               case 2: /* fall-through */
+               case 3:
                        goto out;
-               case 2:
+               case 4:
                        if (last == '.')
                                return -1; /* Refname contains "..". */
                        break;
-               case 3:
+               case 5:
                        if (last == '@')
                                return -1; /* Refname contains "@{". */
                        break;
-               case 4:
+               case 6: /* fall-through */
+               case 7:
                        return -1;
                }
                last = ch;
@@ -79,7 +88,7 @@ static int check_refname_component(const char *refname, int flags)
        return cp - refname;
 }
 
-int check_refname_format(const char *refname, int flags)
+static int check_refname_format_bytewise(const char *refname, int flags)
 {
        int component_len, component_count = 0;
 
@@ -115,6 +124,196 @@ int check_refname_format(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;
 
 /*
@@ -1162,7 +1361,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
@@ -3233,7 +3432,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) {
index 30d2829c2b171f7af8891b5c07a0317c72de1871..3d6c86a36f26406305d58505b10db3f747a2546c 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -170,7 +170,6 @@ static struct branch *make_branch(const char *name, int len)
 {
        struct branch *ret;
        int i;
-       char *refname;
 
        for (i = 0; i < branches_nr; i++) {
                if (len ? (!strncmp(name, branches[i]->name, len) &&
@@ -186,10 +185,7 @@ static struct branch *make_branch(const char *name, int len)
                ret->name = xstrndup(name, len);
        else
                ret->name = xstrdup(name);
-       refname = xmalloc(strlen(name) + strlen("refs/heads/") + 1);
-       strcpy(refname, "refs/heads/");
-       strcpy(refname + strlen("refs/heads/"), ret->name);
-       ret->refname = refname;
+       ret->refname = xstrfmt("refs/heads/%s", ret->name);
 
        return ret;
 }
@@ -1193,7 +1189,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
        case 1:
                break;
        case 0:
-               if (!memcmp(dst_value, "refs/", 5))
+               if (starts_with(dst_value, "refs/"))
                        matched_dst = make_linked_ref(dst_value, dst_tail);
                else if (is_null_sha1(matched_src->new_sha1))
                        error("unable to delete '%s': remote ref does not exist",
index d55aa8a01b4a4f200f24972795f2887644a06d43..ffc6a5b168304726b5fe326211f26dafa460c7fa 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -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 3818b4628d0b5b4fa74428decaa940bef2804856..2571ada6bf66ce6e945c539be4b0123e5b9648a9 100644 (file)
@@ -1649,8 +1649,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->skip_count = atoi(optarg);
                return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
-       /* accept -<digit>, like traditional "head" */
-               revs->max_count = atoi(arg + 1);
+               /* accept -<digit>, like traditional "head" */
+               if (strtol_i(arg + 1, 10, &revs->max_count) < 0 ||
+                   revs->max_count < 0)
+                       die("'%s': not a non-negative integer", arg + 1);
                revs->no_walk = 0;
        } else if (!strcmp(arg, "-n")) {
                if (argc <= 1)
@@ -2789,7 +2791,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
 {
        int retval;
        const char *encoding;
-       char *message;
+       const char *message;
        struct strbuf buf = STRBUF_INIT;
 
        if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
@@ -2831,14 +2833,21 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
                format_display_notes(commit->object.sha1, &buf, encoding, 1);
        }
 
-       /* Find either in the original commit message, or in the temporary */
+       /*
+        * Find either in the original commit message, or in the temporary.
+        * Note that we cast away the constness of "message" here. It is
+        * const because it may come from the cached commit buffer. That's OK,
+        * because we know that it is modifiable heap memory, and that while
+        * grep_buffer may modify it for speed, it will restore any
+        * changes before returning.
+        */
        if (buf.len)
                retval = grep_buffer(&opt->grep_filter, buf.buf, buf.len);
        else
                retval = grep_buffer(&opt->grep_filter,
-                                    message, strlen(message));
+                                    (char *)message, strlen(message));
        strbuf_release(&buf);
-       logmsg_free(message, commit);
+       unuse_commit_buffer(commit, message);
        return retval;
 }
 
index 0a80c58d1194c3476dfd2861a98fbd08610597a3..3c060e054720b7b17f9a868cabb1a0ef62aa1b50 100644 (file)
@@ -116,39 +116,23 @@ static const char *action_name(const struct replay_opts *opts)
        return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
 }
 
-static char *get_encoding(const char *message);
-
 struct commit_message {
        char *parent_label;
        const char *label;
        const char *subject;
-       char *reencoded_message;
        const char *message;
 };
 
 static int get_message(struct commit *commit, struct commit_message *out)
 {
-       const char *encoding;
        const char *abbrev, *subject;
        int abbrev_len, subject_len;
        char *q;
 
-       if (!commit->buffer)
-               return -1;
-       encoding = get_encoding(commit->buffer);
-       if (!encoding)
-               encoding = "UTF-8";
        if (!git_commit_encoding)
                git_commit_encoding = "UTF-8";
 
-       out->reencoded_message = NULL;
-       out->message = commit->buffer;
-       if (same_encoding(encoding, git_commit_encoding))
-               out->reencoded_message = reencode_string(commit->buffer,
-                                       git_commit_encoding, encoding);
-       if (out->reencoded_message)
-               out->message = out->reencoded_message;
-
+       out->message = logmsg_reencode(commit, NULL, git_commit_encoding);
        abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        abbrev_len = strlen(abbrev);
 
@@ -167,29 +151,10 @@ static int get_message(struct commit *commit, struct commit_message *out)
        return 0;
 }
 
-static void free_message(struct commit_message *msg)
+static void free_message(struct commit *commit, struct commit_message *msg)
 {
        free(msg->parent_label);
-       free(msg->reencoded_message);
-}
-
-static char *get_encoding(const char *message)
-{
-       const char *p = message, *eol;
-
-       while (*p && *p != '\n') {
-               for (eol = p + 1; *eol && *eol != '\n'; eol++)
-                       ; /* do nothing */
-               if (starts_with(p, "encoding ")) {
-                       char *result = xmalloc(eol - 8 - p);
-                       strlcpy(result, p + 9, eol - 8 - p);
-                       return result;
-               }
-               p = eol;
-               if (*p == '\n')
-                       p++;
-       }
-       return NULL;
+       unuse_commit_buffer(commit, msg->message);
 }
 
 static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
@@ -278,7 +243,7 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
 
        read_cache();
        if (checkout_fast_forward(from, to, 1))
-               exit(1); /* the callee should have complained already */
+               exit(128); /* the callee should have complained already */
        ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from,
                                           0, NULL);
        if (!ref_lock)
@@ -298,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();
 
@@ -323,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);
@@ -376,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);
@@ -396,18 +358,13 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 {
        struct argv_array array;
        int rc;
-       char *gpg_sign;
 
        argv_array_init(&array);
        argv_array_push(&array, "commit");
        argv_array_push(&array, "-n");
 
-       if (opts->gpg_sign) {
-               gpg_sign = xmalloc(3 + strlen(opts->gpg_sign));
-               sprintf(gpg_sign, "-S%s", opts->gpg_sign);
-               argv_array_push(&array, gpg_sign);
-               free(gpg_sign);
-       }
+       if (opts->gpg_sign)
+               argv_array_pushf(&array, "-S%s", opts->gpg_sign);
        if (opts->signoff)
                argv_array_push(&array, "-s");
        if (!opts->edit) {
@@ -489,7 +446,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        unsigned char head[20];
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
-       struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+       struct commit_message msg = { NULL, NULL, NULL, NULL };
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
        int res, unborn = 0, allow;
@@ -654,7 +611,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                res = run_git_commit(defmsg, opts, allow);
 
 leave:
-       free_message(&msg);
+       free_message(commit, &msg);
        free(defmsg);
 
        return res;
@@ -683,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);
@@ -701,10 +657,12 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
        int subject_len;
 
        for (cur = todo_list; cur; cur = cur->next) {
+               const char *commit_buffer = get_commit_buffer(cur->item, NULL);
                sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
-               subject_len = find_commit_subject(cur->item->buffer, &subject);
+               subject_len = find_commit_subject(commit_buffer, &subject);
                strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
                        subject_len, subject);
+               unuse_commit_buffer(cur->item, commit_buffer);
        }
        return 0;
 }
index 34d527f6708fe242f75b7fa0fa98e36a5c96a3be..4127ae138de6bb002645aa83af00681b1805d982 100644 (file)
@@ -315,7 +315,8 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base, int
         * thing twice, or object directory itself.
         */
        for (alt = alt_odb_list; alt; alt = alt->next) {
-               if (!memcmp(ent->base, alt->base, pfxlen)) {
+               if (pfxlen == alt->name - alt->base - 1 &&
+                   !memcmp(ent->base, alt->base, pfxlen)) {
                        free(ent);
                        return -1;
                }
@@ -1177,48 +1178,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 &&
@@ -1226,24 +1221,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_)
index 72e6ac6a6e4f8573676084e443ce8fae038f4f56..5bfa8416999d35d6bb5c615d86763f013b7df748 100644 (file)
@@ -862,27 +862,17 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
                commit_list_insert(l->item, &backup);
        }
        while (list) {
-               char *p, *to_free = NULL;
+               const char *p, *buf;
                struct commit *commit;
-               enum object_type type;
-               unsigned long size;
                int matches;
 
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
                if (!parse_object(commit->object.sha1))
                        continue;
-               if (commit->buffer)
-                       p = commit->buffer;
-               else {
-                       p = read_sha1_file(commit->object.sha1, &type, &size);
-                       if (!p)
-                               continue;
-                       to_free = p;
-               }
-
-               p = strstr(p, "\n\n");
+               buf = get_commit_buffer(commit, NULL);
+               p = strstr(buf, "\n\n");
                matches = p && !regexec(&regex, p + 2, 0, NULL, 0);
-               free(to_free);
+               unuse_commit_buffer(commit, buf);
 
                if (matches) {
                        hashcpy(sha1, commit->object.sha1);
@@ -1250,10 +1240,7 @@ static void diagnose_invalid_sha1_path(const char *prefix,
                die("Path '%s' exists on disk, but not in '%.*s'.",
                    filename, object_name_len, object_name);
        if (errno == ENOENT || errno == ENOTDIR) {
-               char *fullname = xmalloc(strlen(filename)
-                                            + strlen(prefix) + 1);
-               strcpy(fullname, prefix);
-               strcat(fullname, filename);
+               char *fullname = xstrfmt("%s%s", prefix, filename);
 
                if (!get_tree_entry(tree_sha1, fullname,
                                    sha1, &mode)) {
diff --git a/shell.c b/shell.c
index 5c0d47a5cc1ae85a03e40321e97ddfa859ff7e1a..ace62e4b6503d821962242f9cf3bd9af22bc39b9 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -46,11 +46,7 @@ static int is_valid_cmd_name(const char *cmd)
 
 static char *make_cmd(const char *prog)
 {
-       char *prefix = xmalloc((strlen(prog) + strlen(COMMAND_DIR) + 2));
-       strcpy(prefix, COMMAND_DIR);
-       strcat(prefix, "/");
-       strcat(prefix, prog);
-       return prefix;
+       return xstrfmt("%s/%s", COMMAND_DIR, prog);
 }
 
 static void cd_to_homedir(void)
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 ac62982e672c22457468f601b35716dc9a52a81c..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
@@ -600,3 +591,22 @@ char *xstrdup_tolower(const char *string)
        result[i] = '\0';
        return result;
 }
+
+char *xstrvfmt(const char *fmt, va_list ap)
+{
+       struct strbuf buf = STRBUF_INIT;
+       strbuf_vaddf(&buf, fmt, ap);
+       return strbuf_detach(&buf, NULL);
+}
+
+char *xstrfmt(const char *fmt, ...)
+{
+       va_list ap;
+       char *ret;
+
+       va_start(ap, fmt);
+       ret = xstrvfmt(fmt, ap);
+       va_end(ap);
+
+       return ret;
+}
index e9ad03eabe72dc2ee6e7e0086baca71d343264ce..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
@@ -187,4 +196,13 @@ extern int fprintf_ln(FILE *fp, const char *fmt, ...);
 
 char *xstrdup_tolower(const char *);
 
+/*
+ * Create a newly allocated string using printf format. You can do this easily
+ * with a strbuf, but this provides a shortcut to save a few lines.
+ */
+__attribute__((format (printf, 1, 0)))
+char *xstrvfmt(const char *fmt, va_list ap);
+__attribute__((format (printf, 1, 2)))
+char *xstrfmt(const char *fmt, ...);
+
 #endif /* STRBUF_H */
index b80ecacf60dc87025336b6b6d6a51acc9321f429..48e3b44e219dffec6f435f0f25f1b74409fa29a9 100644 (file)
@@ -965,7 +965,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 c2b41a85013eec87a412badc4097f364d547726d..5261e8cf499006c1d84fc42a3e96e4dee7f09ba1 100644 (file)
@@ -35,12 +35,11 @@ static int longest_path_match(const char *name_a, int len_a,
        return match_len;
 }
 
-static struct cache_def default_cache;
+static struct cache_def default_cache = CACHE_DEF_INIT;
 
 static inline void reset_lstat_cache(struct cache_def *cache)
 {
-       cache->path[0] = '\0';
-       cache->len = 0;
+       strbuf_reset(&cache->path);
        cache->flags = 0;
        /*
         * The track_flags and prefix_len_stat_func members is only
@@ -73,7 +72,7 @@ static int lstat_cache_matchlen(struct cache_def *cache,
                                int prefix_len_stat_func)
 {
        int match_len, last_slash, last_slash_dir, previous_slash;
-       int save_flags, max_len, ret;
+       int save_flags, ret;
        struct stat st;
 
        if (cache->track_flags != track_flags ||
@@ -93,14 +92,14 @@ static int lstat_cache_matchlen(struct cache_def *cache,
                 * the 2 "excluding" path types.
                 */
                match_len = last_slash =
-                       longest_path_match(name, len, cache->path, cache->len,
-                                          &previous_slash);
+                       longest_path_match(name, len, cache->path.buf,
+                                          cache->path.len, &previous_slash);
                *ret_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
 
                if (!(track_flags & FL_FULLPATH) && match_len == len)
                        match_len = last_slash = previous_slash;
 
-               if (*ret_flags && match_len == cache->len)
+               if (*ret_flags && match_len == cache->path.len)
                        return match_len;
                /*
                 * If we now have match_len > 0, we would know that
@@ -121,21 +120,22 @@ static int lstat_cache_matchlen(struct cache_def *cache,
         */
        *ret_flags = FL_DIR;
        last_slash_dir = last_slash;
-       max_len = len < PATH_MAX ? len : PATH_MAX;
-       while (match_len < max_len) {
+       if (len > cache->path.len)
+               strbuf_grow(&cache->path, len - cache->path.len);
+       while (match_len < len) {
                do {
-                       cache->path[match_len] = name[match_len];
+                       cache->path.buf[match_len] = name[match_len];
                        match_len++;
-               } while (match_len < max_len && name[match_len] != '/');
-               if (match_len >= max_len && !(track_flags & FL_FULLPATH))
+               } while (match_len < len && name[match_len] != '/');
+               if (match_len >= len && !(track_flags & FL_FULLPATH))
                        break;
                last_slash = match_len;
-               cache->path[last_slash] = '\0';
+               cache->path.buf[last_slash] = '\0';
 
                if (last_slash <= prefix_len_stat_func)
-                       ret = stat(cache->path, &st);
+                       ret = stat(cache->path.buf, &st);
                else
-                       ret = lstat(cache->path, &st);
+                       ret = lstat(cache->path.buf, &st);
 
                if (ret) {
                        *ret_flags = FL_LSTATERR;
@@ -158,12 +158,11 @@ static int lstat_cache_matchlen(struct cache_def *cache,
         * for the moment!
         */
        save_flags = *ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
-       if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
-               cache->path[last_slash] = '\0';
-               cache->len = last_slash;
+       if (save_flags && last_slash > 0) {
+               cache->path.buf[last_slash] = '\0';
+               cache->path.len = last_slash;
                cache->flags = save_flags;
-       } else if ((track_flags & FL_DIR) &&
-                  last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
+       } else if ((track_flags & FL_DIR) && last_slash_dir > 0) {
                /*
                 * We have a separate test for the directory case,
                 * since it could be that we have found a symlink or a
@@ -175,8 +174,8 @@ static int lstat_cache_matchlen(struct cache_def *cache,
                 * can still cache the path components before the last
                 * one (the found symlink or non-existing component).
                 */
-               cache->path[last_slash_dir] = '\0';
-               cache->len = last_slash_dir;
+               cache->path.buf[last_slash_dir] = '\0';
+               cache->path.len = last_slash_dir;
                cache->flags = FL_DIR;
        } else {
                reset_lstat_cache(cache);
@@ -273,21 +272,18 @@ static int threaded_has_dirs_only_path(struct cache_def *cache, const char *name
                FL_DIR;
 }
 
-static struct removal_def {
-       char path[PATH_MAX];
-       int len;
-} removal;
+static struct strbuf removal = STRBUF_INIT;
 
 static void do_remove_scheduled_dirs(int new_len)
 {
        while (removal.len > new_len) {
-               removal.path[removal.len] = '\0';
-               if (rmdir(removal.path))
+               removal.buf[removal.len] = '\0';
+               if (rmdir(removal.buf))
                        break;
                do {
                        removal.len--;
                } while (removal.len > new_len &&
-                        removal.path[removal.len] != '/');
+                        removal.buf[removal.len] != '/');
        }
        removal.len = new_len;
 }
@@ -297,7 +293,7 @@ void schedule_dir_for_removal(const char *name, int len)
        int match_len, last_slash, i, previous_slash;
 
        match_len = last_slash = i =
-               longest_path_match(name, len, removal.path, removal.len,
+               longest_path_match(name, len, removal.buf, removal.len,
                                   &previous_slash);
        /* Find last slash inside 'name' */
        while (i < len) {
@@ -317,11 +313,8 @@ void schedule_dir_for_removal(const char *name, int len)
         * If we go deeper down the directory tree, we only need to
         * save the new path components as we go down.
         */
-       if (match_len < last_slash) {
-               memcpy(&removal.path[match_len], &name[match_len],
-                      last_slash - match_len);
-               removal.len = last_slash;
-       }
+       if (match_len < last_slash)
+               strbuf_add(&removal, &name[match_len], last_slash - match_len);
 }
 
 void remove_scheduled_dirs(void)
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
index 272fceef96c06e6e104d6341f36b1764e7d0c8db..fd53b5718780e25f03924e38438706acc54598db 100644 (file)
@@ -142,7 +142,7 @@ prepare_httpd() {
        HTTPD_URL_USER=$HTTPD_PROTO://user%40host@$HTTPD_DEST
        HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:pass%40host@$HTTPD_DEST
 
-       if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN"
+       if test -n "$LIB_HTTPD_DAV" || test -n "$LIB_HTTPD_SVN"
        then
                HTTPD_PARA="$HTTPD_PARA -DDAV"
 
index eafc9d2d90ff7646062edd9a376839e469334760..a77b8e546926308fbe348bf01d625bad4996a095 100755 (executable)
@@ -19,6 +19,10 @@ case "$PATH_INFO" in
        printf "text/plain; charset=utf-16"
        charset=utf-16
        ;;
+*odd-spacing*)
+       printf "text/plain; foo=bar ;charset=utf-16; other=nonsense"
+       charset=utf-16
+       ;;
 esac
 printf "\n"
 
index 685d46f8b75ae105d41e1b462c69f980fe1f61a8..f8ed8573b728e82f7f4afaee677758b736048ebe 100755 (executable)
@@ -8,6 +8,9 @@ test_perf_large_repo
 # note that we do everything through config,
 # since we want to be able to compare bitmap-aware
 # git versus non-bitmap git
+#
+# We intentionally use the deprecated pack.writebitmaps
+# config so that we can test against older versions of git.
 test_expect_success 'setup bitmap config' '
        git config pack.writebitmaps true &&
        git config pack.writebitmaphashcache true
index 2f3020342a75f0d382af88e6ad35691ad0f183d5..e62c0ffbc22cffffd9c29254a96222bfb5b85633 100755 (executable)
@@ -56,7 +56,7 @@ test_expect_success 'plain through aliased command, outside any git repo' '
        check_config plain-aliased/.git false unset
 '
 
-test_expect_failure 'plain nested through aliased command' '
+test_expect_success 'plain nested through aliased command' '
        (
                git init plain-ancestor-aliased &&
                cd plain-ancestor-aliased &&
@@ -68,7 +68,7 @@ test_expect_failure 'plain nested through aliased command' '
        check_config plain-ancestor-aliased/plain-nested/.git false unset
 '
 
-test_expect_failure 'plain nested in bare through aliased command' '
+test_expect_success 'plain nested in bare through aliased command' '
        (
                git init --bare bare-ancestor-aliased.git &&
                cd bare-ancestor-aliased.git &&
index 5ef5ad3db4f6c307e6bcf71f57dcdc4deaafc082..39e55a13c885399150094f9a2774f85e0acd63a6 100755 (executable)
@@ -816,12 +816,14 @@ test_expect_success NOT_MINGW,NOT_CYGWIN 'correct handling of backslashes' '
        >"whitespace/trailing 5 \\ \\ " &&
        >"whitespace/trailing 6 \\a\\" &&
        >whitespace/untracked &&
-       echo "whitespace/trailing 1 \\    " >ignore  &&
-       echo "whitespace/trailing 2 \\\\\\\\\\\\\\\\" >>ignore &&
-       echo "whitespace/trailing 3 \\\\\\\\\\\\\\\\ " >>ignore &&
-       echo "whitespace/trailing 4   \\\\\\\\\\\\    " >>ignore &&
-       echo "whitespace/trailing 5 \\\\\\\\ \\\\\\\\\\\\   " >>ignore &&
-       echo "whitespace/trailing 6 \\\\\\\\a\\\\\\\\" >>ignore &&
+       sed -e "s/Z$//" >ignore <<-\EOF &&
+       whitespace/trailing 1 \    Z
+       whitespace/trailing 2 \\\\Z
+       whitespace/trailing 3 \\\\ Z
+       whitespace/trailing 4   \\\    Z
+       whitespace/trailing 5 \\ \\\   Z
+       whitespace/trailing 6 \\a\\Z
+       EOF
        echo whitespace/untracked >expect &&
        >err.expect &&
        git ls-files -o -X ignore whitespace >actual 2>err &&
index b92e6cb0469ebb2e328c490cba9b8aaa7c1dbb84..f890c54d137aab021e6d6a61c5741af872555c1f 100755 (executable)
@@ -190,8 +190,6 @@ test_expect_success 'required filter clean failure' '
        test_must_fail git add test.fc
 '
 
-test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
-
 test_expect_success EXPENSIVE 'filter large file' '
        git config filter.largefile.smudge cat &&
        git config filter.largefile.clean cat &&
index b0e5694ebd07b59e80823cd87825cc9ec8187c21..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" -a -z "$twodiff" -a -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" -a -z "$twodiff" -a -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" -a -n "$twodiff" -a -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
index e1126aa7cc05ff61ddf087fe13880da7f48219a6..4807b0f015d4b87aa762c0f7585d863a1dc34514 100755 (executable)
@@ -36,7 +36,7 @@ test_expect_success 'eol=lf puts LFs in normalized file' '
        ! has_cr two &&
        onediff=$(git diff one) &&
        twodiff=$(git diff two) &&
-       test -z "$onediff" -a -z "$twodiff"
+       test -z "$onediff" && test -z "$twodiff"
 '
 
 test_expect_success 'eol=crlf puts CRLFs in normalized file' '
@@ -49,7 +49,7 @@ test_expect_success 'eol=crlf puts CRLFs in normalized file' '
        ! has_cr two &&
        onediff=$(git diff one) &&
        twodiff=$(git diff two) &&
-       test -z "$onediff" -a -z "$twodiff"
+       test -z "$onediff" && test -z "$twodiff"
 '
 
 test_expect_success 'autocrlf=true overrides eol=lf' '
@@ -63,7 +63,7 @@ test_expect_success 'autocrlf=true overrides eol=lf' '
        has_cr two &&
        onediff=$(git diff one) &&
        twodiff=$(git diff two) &&
-       test -z "$onediff" -a -z "$twodiff"
+       test -z "$onediff" && test -z "$twodiff"
 '
 
 test_expect_success 'autocrlf=true overrides unset eol' '
@@ -77,7 +77,7 @@ test_expect_success 'autocrlf=true overrides unset eol' '
        has_cr two &&
        onediff=$(git diff one) &&
        twodiff=$(git diff two) &&
-       test -z "$onediff" -a -z "$twodiff"
+       test -z "$onediff" && test -z "$twodiff"
 '
 
 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
index 1a5a5f39fd94f9eb98824b3d84983782b9ff71bb..9aeb352b3dba3fd7e46a5a0732425cd2686fc9f3 100755 (executable)
@@ -64,6 +64,7 @@ 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"
@@ -128,6 +129,20 @@ 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 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 e35d7811acc60415e38e58866b5488938d0372a4..8d44e04354f05856465904700ff0374dae74ba59 100755 (executable)
@@ -7,9 +7,7 @@ test_description='Test commit notes index (expensive!)'
 
 . ./test-lib.sh
 
-test_set_prereq NOT_EXPENSIVE
 test -n "$GIT_NOTES_TIMING_TESTS" && test_set_prereq EXPENSIVE
-test -x /usr/bin/time && test_set_prereq USR_BIN_TIME
 
 create_repo () {
        number_of_commits=$1
@@ -17,43 +15,43 @@ create_repo () {
        test -d .git || {
        git init &&
        (
-               while [ $nr -lt $number_of_commits ]; do
+               while test $nr -lt $number_of_commits
+               do
                        nr=$(($nr+1))
                        mark=$(($nr+$nr))
                        notemark=$(($mark+1))
                        test_tick &&
-                       cat <<INPUT_END &&
-commit refs/heads/master
-mark :$mark
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-commit #$nr
-COMMIT
-
-M 644 inline file
-data <<EOF
-file in commit #$nr
-EOF
-
-blob
-mark :$notemark
-data <<EOF
-note for commit #$nr
-EOF
-
-INPUT_END
-
-                       echo "N :$notemark :$mark" >> note_commit
+                       cat <<-INPUT_END &&
+                       commit refs/heads/master
+                       mark :$mark
+                       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+                       data <<COMMIT
+                       commit #$nr
+                       COMMIT
+
+                       M 644 inline file
+                       data <<EOF
+                       file in commit #$nr
+                       EOF
+
+                       blob
+                       mark :$notemark
+                       data <<EOF
+                       note for commit #$nr
+                       EOF
+
+                       INPUT_END
+                       echo "N :$notemark :$mark" >>note_commit
                done &&
                test_tick &&
-               cat <<INPUT_END &&
-commit refs/notes/commits
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-notes
-COMMIT
+               cat <<-INPUT_END &&
+               commit refs/notes/commits
+               committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+               data <<COMMIT
+               notes
+               COMMIT
 
-INPUT_END
+               INPUT_END
 
                cat note_commit
        ) |
@@ -65,62 +63,74 @@ INPUT_END
 test_notes () {
        count=$1 &&
        git config core.notesRef refs/notes/commits &&
-       git log | grep "^    " > output &&
+       git log | grep "^    " >output &&
        i=$count &&
-       while [ $i -gt 0 ]; do
+       while test $i -gt 0
+       do
                echo "    commit #$i" &&
                echo "    note for commit #$i" &&
-               i=$(($i-1));
-       done > expect &&
+               i=$(($i-1))
+       done >expect &&
        test_cmp expect output
 }
 
-cat > time_notes << \EOF
+write_script time_notes <<\EOF
        mode=$1
        i=1
-       while [ $i -lt $2 ]; do
+       while test $i -lt $2
+       do
                case $1 in
                no-notes)
-                       GIT_NOTES_REF=non-existing; export GIT_NOTES_REF
-               ;;
+                       GIT_NOTES_REF=non-existing
+                       export GIT_NOTES_REF
+                       ;;
                notes)
                        unset GIT_NOTES_REF
-               ;;
+                       ;;
                esac
-               git log >/dev/null
+               git log
                i=$(($i+1))
-       done
+       done >/dev/null
 EOF
 
 time_notes () {
        for mode in no-notes notes
        do
                echo $mode
-               /usr/bin/time "$SHELL_PATH" ../time_notes $mode $1
+               /usr/bin/time ../time_notes $mode $1
        done
 }
 
 do_tests () {
-       pr=$1
-       count=$2
-
-       test_expect_success $pr 'setup / mkdir' '
-               mkdir $count &&
-               cd $count
+       count=$1 pr=${2-}
+
+       test_expect_success $pr "setup $count" '
+               mkdir "$count" &&
+               (
+                       cd "$count" &&
+                       create_repo "$count"
+               )
        '
 
-       test_expect_success $pr "setup $count" "create_repo $count"
-
-       test_expect_success $pr 'notes work' "test_notes $count"
-
-       test_expect_success USR_BIN_TIME,$pr 'notes timing with /usr/bin/time' "time_notes 100"
+       test_expect_success $pr 'notes work' '
+               (
+                       cd "$count" &&
+                       test_notes "$count"
+               )
+       '
 
-       test_expect_success $pr 'teardown / cd ..' 'cd ..'
+       test_expect_success "USR_BIN_TIME${pr:+,$pr}" 'notes timing with /usr/bin/time' '
+               (
+                       cd "$count" &&
+                       time_notes 100
+               )
+       '
 }
 
-do_tests NOT_EXPENSIVE 10
-for count in 100 1000 10000; do
-       do_tests EXPENSIVE $count
+do_tests 10
+for count in 100 1000 10000
+do
+       do_tests "$count" EXPENSIVE
 done
 
 test_done
index be8c1d5ef9fd650c128a9fe7126cbcd8db6c5a95..5a27ec9b5eb7c37145a098d878432948233ba756 100755 (executable)
@@ -33,6 +33,7 @@ test_expect_success setup '
        tr "[a-z]" "[A-Z]" <original >newfile &&
        git add newfile &&
        git commit -a -m"side edits further." &&
+       git branch second-side &&
 
        tr "[a-m]" "[A-M]" <original >newfile &&
        rm -f original &&
@@ -41,6 +42,7 @@ test_expect_success setup '
        git branch test-rebase side &&
        git branch test-rebase-pick side &&
        git branch test-reference-pick side &&
+       git branch test-conflicts side &&
        git checkout -b test-merge side
 '
 
@@ -138,4 +140,17 @@ test_expect_success 'rebase -s funny -Xopt' '
        test -f funny.was.run
 '
 
+test_expect_success 'rebase --skip works with two conflicts in a row' '
+       git checkout second-side  &&
+       tr "[A-Z]" "[a-z]" <newfile >tmp &&
+       mv tmp newfile &&
+       git commit -a -m"edit conflicting with side" &&
+       tr "[d-f]" "[D-F]" <newfile >tmp &&
+       mv tmp newfile &&
+       git commit -a -m"another edit conflicting with side" &&
+       test_must_fail git rebase --merge test-conflicts &&
+       test_must_fail git rebase --skip &&
+       git rebase --skip
+'
+
 test_done
index e70ac10a0cdbcd112b7b3c3e74aa89b09d46d1df..9292b499f39925b6832bccd826fb43a8818f155a 100755 (executable)
@@ -4,12 +4,9 @@ test_description='git rebase - test patch id computation'
 
 . ./test-lib.sh
 
-test_set_prereq NOT_EXPENSIVE
 test -n "$GIT_PATCHID_TIMING_TESTS" && test_set_prereq EXPENSIVE
-test -x /usr/bin/time && test_set_prereq USR_BIN_TIME
 
-count()
-{
+count () {
        i=0
        while test $i -lt $1
        do
@@ -18,8 +15,7 @@ count()
        done
 }
 
-scramble()
-{
+scramble () {
        i=0
        while read x
        do
@@ -28,12 +24,11 @@ scramble()
                        echo "$x"
                fi
                i=$((($i+1) % 10))
-       done < "$1" > "$1.new"
+       done <"$1" >"$1.new"
        mv -f "$1.new" "$1"
 }
 
-run()
-{
+run () {
        echo \$ "$@"
        /usr/bin/time "$@" >/dev/null
 }
@@ -43,10 +38,8 @@ test_expect_success 'setup' '
        git tag root
 '
 
-do_tests()
-{
-       pr=$1
-       nlines=$2
+do_tests () {
+       nlines=$1 pr=${2-}
 
        test_expect_success $pr "setup: $nlines lines" "
                rm -f .gitattributes &&
@@ -103,7 +96,7 @@ do_tests()
        "
 }
 
-do_tests NOT_EXPENSIVE 500
-do_tests EXPENSIVE 50000
+do_tests 500
+do_tests 50000 EXPENSIVE
 
 test_done
index 49e2d6c34983c7e4620739a52ecc03cf158efe3a..fae305979a88614bdf33963936d3fce32df83b8c 100755 (executable)
@@ -52,6 +52,6 @@ EOF
 
 test_expect_success 'apply copy' \
     'git apply --index --stat --summary --apply test-patch &&
-     test "$(cat bar)" = "This is bar" -a "$(cat foo)" = "This is foo"'
+     test "$(cat bar)" = "This is bar" && test "$(cat foo)" = "This is foo"'
 
 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
index bad84a36e81dbebbad89bf66cb1411ebf27fe66a..7b8babd89b22dc94d3a3f839d72cce86c5aaea59 100755 (executable)
@@ -72,7 +72,7 @@ check_tar() {
                        for header in *.paxheader
                        do
                                data=${header%.paxheader}.data &&
-                               if test -h $data -o -e $data
+                               if test -h $data || test -e $data
                                then
                                        path=$(get_pax_header $header path) &&
                                        if test -n "$path"
@@ -119,14 +119,10 @@ test_expect_success \
     'echo ignore me >a/ignored &&
      echo ignored export-ignore >.git/info/attributes'
 
-test_expect_success \
-    'add files to repository' \
-    'find a -type f | xargs git update-index --add &&
-     find a -type l | xargs git update-index --add &&
-     treeid=$(git write-tree) &&
-     echo $treeid >treeid &&
-     git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
-     git commit-tree $treeid </dev/null)'
+test_expect_success 'add files to repository' '
+       git add a &&
+       GIT_COMMITTER_DATE="2005-05-27 22:00" git commit -m initial
+'
 
 test_expect_success 'setup export-subst' '
        echo "substfile?" export-subst >>.git/info/attributes &&
index 21a5c93f41e28845a540637eb11bd6eec830a063..c929db563326614e01962b3e322f087640fa19d9 100755 (executable)
@@ -61,14 +61,10 @@ test_expect_success \
     'echo ignore me >a/ignored &&
      echo ignored export-ignore >.git/info/attributes'
 
-test_expect_success \
-    'add files to repository' \
-    'find a -type f | xargs git update-index --add &&
-     find a -type l | xargs git update-index --add &&
-     treeid=`git write-tree` &&
-     echo $treeid >treeid &&
-     git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
-     git commit-tree $treeid </dev/null)'
+test_expect_success 'add files to repository' '
+       git add a &&
+       GIT_COMMITTER_DATE="2005-05-27 22:00" git commit -m initial
+'
 
 test_expect_success 'setup export-subst' '
        echo "substfile?" export-subst >>.git/info/attributes &&
index f4f02ba918535d5c40a30585a54469bc12ab96d3..0580258c91a07aabe2773cce04df47d3c183b425 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success 'setup repo with moderate-sized history' '
        git checkout master &&
        blob=$(echo tagged-blob | git hash-object -w --stdin) &&
        git tag tagged-blob $blob &&
-       git config pack.writebitmaps true &&
+       git config repack.writebitmaps true &&
        git config pack.writebitmaphashcache true
 '
 
index 1753ef2b91e3e5846f12fc16e1209b1895210f3b..fc898c9eac704c934cfbef47fc1463f27dd50c6f 100755 (executable)
@@ -39,7 +39,7 @@ test_expect_success 'post-checkout receives the right arguments with HEAD unchan
        old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
        new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
        flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
-       test $old = $new -a $flag = 1
+       test $old = $new && test $flag = 1
 '
 
 test_expect_success 'post-checkout runs as expected ' '
@@ -52,7 +52,7 @@ test_expect_success 'post-checkout args are correct with git checkout -b ' '
        old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
        new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
        flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
-       test $old = $new -a $flag = 1
+       test $old = $new && test $flag = 1
 '
 
 test_expect_success 'post-checkout receives the right args with HEAD changed ' '
@@ -60,7 +60,7 @@ test_expect_success 'post-checkout receives the right args with HEAD changed ' '
        old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
        new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
        flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
-       test $old != $new -a $flag = 1
+       test $old != $new && test $flag = 1
 '
 
 test_expect_success 'post-checkout receives the right args when not switching branches ' '
@@ -68,7 +68,7 @@ test_expect_success 'post-checkout receives the right args when not switching br
        old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
        new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
        flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
-       test $old = $new -a $flag = 0
+       test $old = $new && test $flag = 0
 '
 
 if test "$(git config --bool core.filemode)" = true; then
index 29d59ef9fa2bf2bffb0075f07e03103387ab718e..d78f3201f423504bf5d53029492cc1bc056aab06 100755 (executable)
@@ -447,6 +447,43 @@ test_expect_success 'explicit pull should update tracking' '
        )
 '
 
+test_expect_success 'explicit --refmap is allowed only with command-line refspec' '
+       cd "$D" &&
+       (
+               cd three &&
+               test_must_fail git fetch --refmap="*:refs/remotes/none/*"
+       )
+'
+
+test_expect_success 'explicit --refmap option overrides remote.*.fetch' '
+       cd "$D" &&
+       git branch -f side &&
+       (
+               cd three &&
+               git update-ref refs/remotes/origin/master base-origin-master &&
+               o=$(git rev-parse --verify refs/remotes/origin/master) &&
+               git fetch --refmap="refs/heads/*:refs/remotes/other/*" origin master &&
+               n=$(git rev-parse --verify refs/remotes/origin/master) &&
+               test "$o" = "$n" &&
+               test_must_fail git rev-parse --verify refs/remotes/origin/side &&
+               git rev-parse --verify refs/remotes/other/master
+       )
+'
+
+test_expect_success 'explicitly empty --refmap option disables remote.*.fetch' '
+       cd "$D" &&
+       git branch -f side &&
+       (
+               cd three &&
+               git update-ref refs/remotes/origin/master base-origin-master &&
+               o=$(git rev-parse --verify refs/remotes/origin/master) &&
+               git fetch --refmap="" origin master &&
+               n=$(git rev-parse --verify refs/remotes/origin/master) &&
+               test "$o" = "$n" &&
+               test_must_fail git rev-parse --verify refs/remotes/origin/side
+       )
+'
+
 test_expect_success 'configured fetch updates tracking' '
 
        cd "$D" &&
index 01b8aae2ed12eab92ecbfb562cb616e0cfc47202..ac71418a1b26bc17a1252d6831869b96a8d8c176 100755 (executable)
@@ -191,5 +191,10 @@ test_expect_success 'http error messages are reencoded' '
        grep "this is the error message" stderr
 '
 
+test_expect_success 'reencoding is robust to whitespace oddities' '
+       test_must_fail git clone "$HTTPD_URL/error/odd-spacing" 2>stderr &&
+       grep "this is the error message" stderr
+'
+
 stop_httpd
 test_done
index e07eaf35f118893da9240f12d115da1affd5eb05..6cbc12d9a7ad059f77967c0bdb702d8a5ec75bb9 100755 (executable)
@@ -213,8 +213,6 @@ test_expect_success 'cookies stored in http.cookiefile when http.savecookies set
        test_cmp expect_cookies.txt cookies_tail.txt
 '
 
-test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
-
 test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
        (
        cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
@@ -240,8 +238,7 @@ test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
 '
 
 test_expect_success EXPENSIVE 'clone the 50,000 tag repo to check OS command line overflow' '
-       git clone $HTTPD_URL/smart/repo.git too-many-refs 2>err &&
-       test_line_count = 0 err &&
+       git clone $HTTPD_URL/smart/repo.git too-many-refs &&
        (
                cd too-many-refs &&
                test $(git for-each-ref refs/tags | wc -l) = 50000
index c277db64f75ac9f9c0cd617262824d76959fbfbd..88ed3191e871cd9a164c3c903f69b07e7d9cf937 100755 (executable)
@@ -468,4 +468,10 @@ test_expect_success 'single-character name is parsed correctly' '
        test_cmp expect actual
 '
 
+test_expect_success 'unused %G placeholders are passed through' '
+       echo "%GX %G" >expect &&
+       git log -1 --format="%GX %G" >actual &&
+       test_cmp expect actual
+'
+
 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
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 74de814aec1465848ba316e867106a18635139f6..04118ad75be8462fa2ee1e5e870feb0a44ac0482 100755 (executable)
@@ -426,10 +426,10 @@ test_expect_success SANITY 'removal failure' '
 
        mkdir foo &&
        touch foo/bar &&
+       test_when_finished "chmod 755 foo" &&
        (exec <foo/bar &&
         chmod 0 foo &&
-        test_must_fail git clean -f -d &&
-        chmod 755 foo)
+        test_must_fail git clean -f -d)
 '
 
 test_expect_success 'nested git work tree' '
index 5ddac1a9f740c03f1d94f75db2c6e54dd3391eda..474dab381aef027207026cb938df7a09cc7a9056 100755 (executable)
@@ -43,44 +43,74 @@ test_expect_success GPG 'create signed commits' '
 
        test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
        git tag seventh-signed
+
+       echo 8 >file && test_tick && git commit -a -m eighth -SB7227189 &&
+       git tag eighth-signed-alt
 '
 
-test_expect_success GPG 'show signatures' '
+test_expect_success GPG 'verify and show signatures' '
        (
-               for commit in initial second merge fourth-signed fifth-signed sixth-signed master
+               for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
                do
+                       git verify-commit $commit &&
                        git show --pretty=short --show-signature $commit >actual &&
-                       grep "Good signature from" actual || exit 1
-                       ! grep "BAD signature from" actual || exit 1
-                       echo $commit OK
+                       grep "Good signature from" actual &&
+                       ! grep "BAD signature from" actual &&
+                       echo $commit OK || exit 1
                done
        ) &&
        (
                for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
                do
+                       test_must_fail git verify-commit $commit &&
                        git show --pretty=short --show-signature $commit >actual &&
-                       grep "Good signature from" actual && exit 1
-                       ! grep "BAD signature from" actual || exit 1
-                       echo $commit OK
+                       ! grep "Good signature from" actual &&
+                       ! grep "BAD signature from" actual &&
+                       echo $commit OK || exit 1
+               done
+       ) &&
+       (
+               for commit in eighth-signed-alt
+               do
+                       git show --pretty=short --show-signature $commit >actual &&
+                       grep "Good signature from" actual &&
+                       ! grep "BAD signature from" actual &&
+                       grep "not certified" actual &&
+                       echo $commit OK || exit 1
                done
        )
 '
 
+test_expect_success GPG 'show signed commit with signature' '
+       git show -s initial >commit &&
+       git show -s --show-signature initial >show &&
+       git verify-commit -v initial >verify.1 2>verify.2 &&
+       git cat-file commit initial >cat &&
+       grep -v "gpg: " show >show.commit &&
+       grep "gpg: " show >show.gpg &&
+       grep -v "^ " cat | grep -v "^gpgsig " >cat.commit &&
+       test_cmp show.commit commit &&
+       test_cmp show.gpg verify.2 &&
+       test_cmp cat.commit verify.1
+'
+
 test_expect_success GPG 'detect fudged signature' '
-       git cat-file commit master >raw &&
+       git cat-file commit seventh-signed >raw &&
 
        sed -e "s/seventh/7th forged/" raw >forged1 &&
        git hash-object -w -t commit forged1 >forged1.commit &&
+       ! git verify-commit $(cat forged1.commit) &&
        git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
        grep "BAD signature from" actual1 &&
        ! grep "Good signature from" actual1
 '
 
 test_expect_success GPG 'detect fudged signature with NUL' '
-       git cat-file commit master >raw &&
+       git cat-file commit seventh-signed >raw &&
        cat raw >forged2 &&
        echo Qwik | tr "Q" "\000" >>forged2 &&
        git hash-object -w -t commit forged2 >forged2.commit &&
+       ! git verify-commit $(cat forged2.commit) &&
        git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
        grep "BAD signature from" actual2 &&
        ! grep "Good signature from" actual2
@@ -89,9 +119,50 @@ test_expect_success GPG 'detect fudged signature with NUL' '
 test_expect_success GPG 'amending already signed commit' '
        git checkout fourth-signed^0 &&
        git commit --amend -S --no-edit &&
+       git verify-commit HEAD &&
        git show -s --show-signature HEAD >actual &&
        grep "Good signature from" actual &&
        ! grep "BAD signature from" actual
 '
 
+test_expect_success GPG 'show good signature with custom format' '
+       cat >expect <<-\EOF &&
+       G
+       13B6F51ECDDE430D
+       C O Mitter <committer@example.com>
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS" sixth-signed >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show bad signature with custom format' '
+       cat >expect <<-\EOF &&
+       B
+       13B6F51ECDDE430D
+       C O Mitter <committer@example.com>
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS" $(cat forged1.commit) >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show unknown signature with custom format' '
+       cat >expect <<-\EOF &&
+       U
+       61092E85B7227189
+       Eris Discordia <discord@example.net>
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show lack of signature with custom format' '
+       cat >expect <<-\EOF &&
+       N
+
+
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS" seventh-unsigned >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 284018e3cdb31e7db179a93f7196fda47578c43c..021c5479bdf4665e815590dc05a0831e7d92c53c 100755 (executable)
@@ -21,7 +21,7 @@ test_expect_success 'objects in packs marked .keep are not repacked' '
        objsha1=$(git verify-pack -v pack-$packsha1.idx | head -n 1 |
                sed -e "s/^\([0-9a-f]\{40\}\).*/\1/") &&
        mv pack-* .git/objects/pack/ &&
-       git repack --no-pack-kept-objects -A -d -l &&
+       git repack -A -d -l &&
        git prune-packed &&
        for p in .git/objects/pack/*.idx; do
                idx=$(basename $p)
@@ -35,9 +35,25 @@ test_expect_success 'objects in packs marked .keep are not repacked' '
        test -z "$found_duplicate_object"
 '
 
-test_expect_success 'writing bitmaps can duplicate .keep objects' '
+test_expect_success 'writing bitmaps via command-line can duplicate .keep objects' '
        # build on $objsha1, $packsha1, and .keep state from previous
-       git repack -Adl &&
+       git repack -Adbl &&
+       test_when_finished "found_duplicate_object=" &&
+       for p in .git/objects/pack/*.idx; do
+               idx=$(basename $p)
+               test "pack-$packsha1.idx" = "$idx" && continue
+               if git verify-pack -v $p | egrep "^$objsha1"; then
+                       found_duplicate_object=1
+                       echo "DUPLICATE OBJECT FOUND"
+                       break
+               fi
+       done &&
+       test "$found_duplicate_object" = 1
+'
+
+test_expect_success 'writing bitmaps via config can duplicate .keep objects' '
+       # build on $objsha1, $packsha1, and .keep state from previous
+       git -c repack.writebitmaps=true repack -Adl &&
        test_when_finished "found_duplicate_object=" &&
        for p in .git/objects/pack/*.idx; do
                idx=$(basename $p)
index 1ecdacb6fd0ceb7e43e6ec3bad018e7b6dc8100b..19a3ced600a20d805d430533b275a8fa4269b835 100755 (executable)
@@ -1334,6 +1334,51 @@ test_expect_success $PREREQ '--force sends cover letter template anyway' '
        test -n "$(ls msgtxt*)"
 '
 
+test_cover_addresses () {
+       header="$1"
+       shift
+       clean_fake_sendmail &&
+       rm -fr outdir &&
+       git format-patch --cover-letter -2 -o outdir &&
+       cover=`echo outdir/0000-*.patch` &&
+       mv $cover cover-to-edit.patch &&
+       perl -pe "s/^From:/$header: extra\@address.com\nFrom:/" cover-to-edit.patch >"$cover" &&
+       git send-email \
+         --force \
+         --from="Example <nobody@example.com>" \
+         --no-to --no-cc \
+         "$@" \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         outdir/0000-*.patch \
+         outdir/0001-*.patch \
+         outdir/0002-*.patch \
+         2>errors >out &&
+       grep "^$header: extra@address.com" msgtxt1 >to1 &&
+       grep "^$header: extra@address.com" msgtxt2 >to2 &&
+       grep "^$header: extra@address.com" msgtxt3 >to3 &&
+       test_line_count = 1 to1 &&
+       test_line_count = 1 to2 &&
+       test_line_count = 1 to3
+}
+
+test_expect_success $PREREQ 'to-cover adds To to all mail' '
+       test_cover_addresses "To" --to-cover
+'
+
+test_expect_success $PREREQ 'cc-cover adds Cc to all mail' '
+       test_cover_addresses "Cc" --cc-cover
+'
+
+test_expect_success $PREREQ 'tocover adds To to all mail' '
+       test_config sendemail.tocover true &&
+       test_cover_addresses "To"
+'
+
+test_expect_success $PREREQ 'cccover adds Cc to all mail' '
+       test_config sendemail.cccover true &&
+       test_cover_addresses "Cc"
+'
+
 test_expect_success $PREREQ 'sendemail.aliasfiletype=mailrc' '
        clean_fake_sendmail &&
        echo "alias sbd  somebody@example.org" >.mailrc &&
index be802e0e16f165f92d3526572a483d1287b19ef9..1fc1f5f2afcf205f10d04cd1deaf5708f727332e 100755 (executable)
@@ -177,7 +177,7 @@ 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 -o "$src" = file11 &&
+               test "$src" = file10 || test "$src" = file11 &&
                git config git-p4.detectCopies $(($level + 2)) &&
                git p4 submit &&
                p4 filelog //depot/file12 &&
@@ -191,7 +191,7 @@ 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 -o "$src" = file11 -o "$src" = file12 &&
+               test "$src" = file10 || test "$src" = file11 || test "$src" = file12 &&
                git config git-p4.detectCopies $(($level - 2)) &&
                git p4 submit &&
                p4 filelog //depot/file13 &&
index 2d4beb5e50c257fed2f4b6475cb5834425b5a64f..1d1c1063a38866f1792f9b451a4942d06c91edb4 100755 (executable)
@@ -550,6 +550,33 @@ test_expect_success 'complete files' '
        test_completion "git add mom" "momified"
 '
 
+test_expect_success "completion uses <cmd> completion for alias: !sh -c 'git <cmd> ...'" '
+       test_config alias.co "!sh -c '"'"'git checkout ...'"'"'" &&
+       test_completion "git co m" <<-\EOF
+       master Z
+       mybranch Z
+       mytag Z
+       EOF
+'
+
+test_expect_success 'completion uses <cmd> completion for alias: !f () { VAR=val git <cmd> ... }' '
+       test_config alias.co "!f () { VAR=val git checkout ... ; } f" &&
+       test_completion "git co m" <<-\EOF
+       master Z
+       mybranch Z
+       mytag Z
+       EOF
+'
+
+test_expect_success 'completion used <cmd> completion for alias: !f() { : git <cmd> ; ... }' '
+       test_config alias.co "!f() { : git checkout ; if ... } f" &&
+       test_completion "git co m" <<-\EOF
+       master Z
+       mybranch Z
+       mytag Z
+       EOF
+'
+
 test_expect_failure 'complete with tilde expansion' '
        git init tmp && cd tmp &&
        test_when_finished "cd .. && rm -rf tmp" &&
index c617c826db94f2e3c0ed01fe079d3ae14ac3b883..0377d3e2960cc282ce1a2c7cb629245728c82092 100644 (file)
@@ -542,7 +542,7 @@ test_must_fail () {
        if test $exit_code = 0; then
                echo >&2 "test_must_fail: command succeeded: $*"
                return 1
-       elif test $exit_code -gt 129 -a $exit_code -le 192; then
+       elif test $exit_code -gt 129 && test $exit_code -le 192; then
                echo >&2 "test_must_fail: died by signal: $*"
                return 1
        elif test $exit_code = 127; then
@@ -569,7 +569,7 @@ test_must_fail () {
 test_might_fail () {
        "$@"
        exit_code=$?
-       if test $exit_code -gt 129 -a $exit_code -le 192; then
+       if test $exit_code -gt 129 && test $exit_code -le 192; then
                echo >&2 "test_might_fail: died by signal: $*"
                return 1
        elif test $exit_code = 127; then
index 81394c8c7c859b1a46a765c05020e74bf0e2808e..a4795373a6a2e1f247b3cef9880f2a82309b3594 100644 (file)
@@ -976,6 +976,14 @@ test_lazy_prereq AUTOIDENT '
        git var GIT_AUTHOR_IDENT
 '
 
+test_lazy_prereq EXPENSIVE '
+       test -n "$GIT_TEST_LONG"
+'
+
+test_lazy_prereq USR_BIN_TIME '
+       test -x /usr/bin/time
+'
+
 # When the tests are run as root, permission tests will report that
 # things are writable when they shouldn't be.
 test -w / || test_set_prereq SANITY
index 0a6724fcc45eed89d612b0e641fd3fe6d4231fcb..9d51c92b74b5b9251470ea3b5d168a4cfbfb9939 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
+}
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 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;
 }
index 4dc86c7fe5f66b858b6bd7834a76b1dbbb63a339..5dd9a718047bc14e9ae840a46d1f71cb435fd6d7 100644 (file)
@@ -144,16 +144,6 @@ struct tree_desc_x {
        struct tree_desc_skip *skip;
 };
 
-static int name_compare(const char *a, int a_len,
-                       const char *b, int b_len)
-{
-       int len = (a_len < b_len) ? a_len : b_len;
-       int cmp = memcmp(a, b, len);
-       if (cmp)
-               return cmp;
-       return (a_len - b_len);
-}
-
 static int check_entry_match(const char *a, int a_len, const char *b, int b_len)
 {
        /*
index 4db78038e43adac119d6b58c55e8cf7a8baa5308..47cdd2369d1919b7b7b267c622ad31e2ad6f24c0 100644 (file)
@@ -6,7 +6,7 @@ static const struct interval zero_width[] = {
 { 0x05C1, 0x05C2 },
 { 0x05C4, 0x05C5 },
 { 0x05C7, 0x05C7 },
-{ 0x0600, 0x0604 },
+{ 0x0600, 0x0605 },
 { 0x0610, 0x061A },
 { 0x061C, 0x061C },
 { 0x064B, 0x065F },
@@ -25,8 +25,7 @@ static const struct interval zero_width[] = {
 { 0x0825, 0x0827 },
 { 0x0829, 0x082D },
 { 0x0859, 0x085B },
-{ 0x08E4, 0x08FE },
-{ 0x0900, 0x0902 },
+{ 0x08E4, 0x0902 },
 { 0x093A, 0x093A },
 { 0x093C, 0x093C },
 { 0x0941, 0x0948 },
@@ -62,16 +61,19 @@ static const struct interval zero_width[] = {
 { 0x0B82, 0x0B82 },
 { 0x0BC0, 0x0BC0 },
 { 0x0BCD, 0x0BCD },
+{ 0x0C00, 0x0C00 },
 { 0x0C3E, 0x0C40 },
 { 0x0C46, 0x0C48 },
 { 0x0C4A, 0x0C4D },
 { 0x0C55, 0x0C56 },
 { 0x0C62, 0x0C63 },
+{ 0x0C81, 0x0C81 },
 { 0x0CBC, 0x0CBC },
 { 0x0CBF, 0x0CBF },
 { 0x0CC6, 0x0CC6 },
 { 0x0CCC, 0x0CCD },
 { 0x0CE2, 0x0CE3 },
+{ 0x0D01, 0x0D01 },
 { 0x0D41, 0x0D44 },
 { 0x0D4D, 0x0D4D },
 { 0x0D62, 0x0D63 },
@@ -132,6 +134,7 @@ static const struct interval zero_width[] = {
 { 0x1A65, 0x1A6C },
 { 0x1A73, 0x1A7C },
 { 0x1A7F, 0x1A7F },
+{ 0x1AB0, 0x1ABE },
 { 0x1B00, 0x1B03 },
 { 0x1B34, 0x1B34 },
 { 0x1B36, 0x1B3A },
@@ -141,7 +144,7 @@ static const struct interval zero_width[] = {
 { 0x1B80, 0x1B81 },
 { 0x1BA2, 0x1BA5 },
 { 0x1BA8, 0x1BA9 },
-{ 0x1BAB, 0x1BAB },
+{ 0x1BAB, 0x1BAD },
 { 0x1BE6, 0x1BE6 },
 { 0x1BE8, 0x1BE9 },
 { 0x1BED, 0x1BED },
@@ -153,7 +156,8 @@ static const struct interval zero_width[] = {
 { 0x1CE2, 0x1CE8 },
 { 0x1CED, 0x1CED },
 { 0x1CF4, 0x1CF4 },
-{ 0x1DC0, 0x1DE6 },
+{ 0x1CF8, 0x1CF9 },
+{ 0x1DC0, 0x1DF5 },
 { 0x1DFC, 0x1DFF },
 { 0x200B, 0x200F },
 { 0x202A, 0x202E },
@@ -181,11 +185,13 @@ static const struct interval zero_width[] = {
 { 0xA9B3, 0xA9B3 },
 { 0xA9B6, 0xA9B9 },
 { 0xA9BC, 0xA9BC },
+{ 0xA9E5, 0xA9E5 },
 { 0xAA29, 0xAA2E },
 { 0xAA31, 0xAA32 },
 { 0xAA35, 0xAA36 },
 { 0xAA43, 0xAA43 },
 { 0xAA4C, 0xAA4C },
+{ 0xAA7C, 0xAA7C },
 { 0xAAB0, 0xAAB0 },
 { 0xAAB2, 0xAAB4 },
 { 0xAAB7, 0xAAB8 },
@@ -198,36 +204,65 @@ static const struct interval zero_width[] = {
 { 0xABED, 0xABED },
 { 0xFB1E, 0xFB1E },
 { 0xFE00, 0xFE0F },
-{ 0xFE20, 0xFE26 },
+{ 0xFE20, 0xFE2D },
 { 0xFEFF, 0xFEFF },
 { 0xFFF9, 0xFFFB },
 { 0x101FD, 0x101FD },
+{ 0x102E0, 0x102E0 },
+{ 0x10376, 0x1037A },
 { 0x10A01, 0x10A03 },
 { 0x10A05, 0x10A06 },
 { 0x10A0C, 0x10A0F },
 { 0x10A38, 0x10A3A },
 { 0x10A3F, 0x10A3F },
+{ 0x10AE5, 0x10AE6 },
 { 0x11001, 0x11001 },
 { 0x11038, 0x11046 },
-{ 0x11080, 0x11081 },
+{ 0x1107F, 0x11081 },
 { 0x110B3, 0x110B6 },
 { 0x110B9, 0x110BA },
 { 0x110BD, 0x110BD },
 { 0x11100, 0x11102 },
 { 0x11127, 0x1112B },
 { 0x1112D, 0x11134 },
+{ 0x11173, 0x11173 },
 { 0x11180, 0x11181 },
 { 0x111B6, 0x111BE },
+{ 0x1122F, 0x11231 },
+{ 0x11234, 0x11234 },
+{ 0x11236, 0x11237 },
+{ 0x112DF, 0x112DF },
+{ 0x112E3, 0x112EA },
+{ 0x11301, 0x11301 },
+{ 0x1133C, 0x1133C },
+{ 0x11340, 0x11340 },
+{ 0x11366, 0x1136C },
+{ 0x11370, 0x11374 },
+{ 0x114B3, 0x114B8 },
+{ 0x114BA, 0x114BA },
+{ 0x114BF, 0x114C0 },
+{ 0x114C2, 0x114C3 },
+{ 0x115B2, 0x115B5 },
+{ 0x115BC, 0x115BD },
+{ 0x115BF, 0x115C0 },
+{ 0x11633, 0x1163A },
+{ 0x1163D, 0x1163D },
+{ 0x1163F, 0x11640 },
 { 0x116AB, 0x116AB },
 { 0x116AD, 0x116AD },
 { 0x116B0, 0x116B5 },
 { 0x116B7, 0x116B7 },
+{ 0x16AF0, 0x16AF4 },
+{ 0x16B30, 0x16B36 },
 { 0x16F8F, 0x16F92 },
+{ 0x1BC9D, 0x1BC9E },
+{ 0x1BCA0, 0x1BCA3 },
 { 0x1D167, 0x1D169 },
 { 0x1D173, 0x1D182 },
 { 0x1D185, 0x1D18B },
 { 0x1D1AA, 0x1D1AD },
 { 0x1D242, 0x1D244 },
+{ 0x1E8D0, 0x1E8D6 },
 { 0xE0001, 0xE0001 },
 { 0xE0020, 0xE007F },
 { 0xE0100, 0xE01EF }
index 97fc9954672f2c374541cad827bb5c459abebcdc..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
@@ -56,17 +57,15 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
        int i;
        const char **msgs = opts->msgs;
        const char *msg;
-       char *tmp;
        const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
+
        if (advice_commit_before_merge)
                msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
                        "Please, commit your changes or stash them before you can %s.";
        else
                msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
-       sprintf(tmp, msg, cmd, cmd2);
-       msgs[ERROR_WOULD_OVERWRITE] = tmp;
-       msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
+       msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
+               xstrfmt(msg, cmd, cmd2);
 
        msgs[ERROR_NOT_UPTODATE_DIR] =
                "Updating the following directories would lose untracked files in it:\n%s";
@@ -76,12 +75,9 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
                        "Please move or remove them before you can %s.";
        else
                msg = "The following untracked working tree files would be %s by %s:\n%%s";
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
-       sprintf(tmp, msg, "removed", cmd, cmd2);
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
-       sprintf(tmp, msg, "overwritten", cmd, cmd2);
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
+
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, "removed", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, "overwritten", cmd, cmd2);
 
        /*
         * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
@@ -246,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);
 
@@ -254,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()) {
@@ -622,17 +624,6 @@ static int unpack_failed(struct unpack_trees_options *o, const char *message)
        return -1;
 }
 
-/* NEEDSWORK: give this a better name and share with tree-walk.c */
-static int name_compare(const char *a, int a_len,
-                       const char *b, int b_len)
-{
-       int len = (a_len < b_len) ? a_len : b_len;
-       int cmp = memcmp(a, b, len);
-       if (cmp)
-               return cmp;
-       return (a_len - b_len);
-}
-
 /*
  * The tree traversal is looking at name p.  If we have a matching entry,
  * return it.  If name p is a directory in the index, do not return
@@ -1025,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)
@@ -1041,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);
 
@@ -1131,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;
@@ -1259,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);
 }
 
 /*
index 1dd86b8f33e04cfc6c0616578416acb1160b5718..014826464e07c93374868c61673cfe308961dfb7 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -253,7 +253,8 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 {
        struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *));
        unsigned char *sha1 = xmalloc(targets * 20);
-       char *msg;
+       const char *msg;
+       char *to_free = NULL;
        int ret;
        int i;
 
@@ -285,21 +286,19 @@ int walker_fetch(struct walker *walker, int targets, char **target,
        if (loop(walker))
                goto unlock_and_fail;
 
-       if (write_ref_log_details) {
-               msg = xmalloc(strlen(write_ref_log_details) + 12);
-               sprintf(msg, "fetch from %s", write_ref_log_details);
-       } else {
-               msg = NULL;
-       }
+       if (write_ref_log_details)
+               msg = to_free = xstrfmt("fetch from %s", write_ref_log_details);
+       else
+               msg = "fetch (unknown)";
        for (i = 0; i < targets; i++) {
                if (!write_ref || !write_ref[i])
                        continue;
-               ret = write_ref_sha1(lock[i], &sha1[20 * i], msg ? msg : "fetch (unknown)");
+               ret = write_ref_sha1(lock[i], &sha1[20 * i], msg);
                lock[i] = NULL;
                if (ret)
                        goto unlock_and_fail;
        }
-       free(msg);
+       free(to_free);
 
        return 0;
 
@@ -307,6 +306,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
        for (i = 0; i < targets; i++)
                if (lock[i])
                        unlock_ref(lock[i]);
+       free(to_free);
 
        return -1;
 }
index 318a191238fb831da8b72f04886b311f1c2c2567..882cfe9fb050efe84e2330b9aea76685c6a7f891 100644 (file)
@@ -734,37 +734,34 @@ static void wt_status_print_changed(struct wt_status *s)
 static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
 {
        struct child_process sm_summary;
-       char summary_limit[64];
-       char index[PATH_MAX];
-       const char *env[] = { NULL, NULL };
+       struct argv_array env = ARGV_ARRAY_INIT;
        struct argv_array argv = ARGV_ARRAY_INIT;
        struct strbuf cmd_stdout = STRBUF_INIT;
        struct strbuf summary = STRBUF_INIT;
        char *summary_content;
        size_t len;
 
-       sprintf(summary_limit, "%d", s->submodule_summary);
-       snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
+       argv_array_pushf(&env, "GIT_INDEX_FILE=%s", s->index_file);
 
-       env[0] = index;
        argv_array_push(&argv, "submodule");
        argv_array_push(&argv, "summary");
        argv_array_push(&argv, uncommitted ? "--files" : "--cached");
        argv_array_push(&argv, "--for-status");
        argv_array_push(&argv, "--summary-limit");
-       argv_array_push(&argv, summary_limit);
+       argv_array_pushf(&argv, "%d", s->submodule_summary);
        if (!uncommitted)
                argv_array_push(&argv, s->amend ? "HEAD^" : "HEAD");
 
        memset(&sm_summary, 0, sizeof(sm_summary));
        sm_summary.argv = argv.argv;
-       sm_summary.env = env;
+       sm_summary.env = env.argv;
        sm_summary.git_cmd = 1;
        sm_summary.no_stdin = 1;
        fflush(s->fp);
        sm_summary.out = -1;
 
        run_command(&sm_summary);
+       argv_array_clear(&env);
        argv_array_clear(&argv);
 
        len = strbuf_read(&cmd_stdout, sm_summary.out, 1024);
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;