summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.mailmap4
-rw-r--r--Documentation/CodingGuidelines9
-rw-r--r--Documentation/RelNotes/2.0.1.txt115
-rw-r--r--Documentation/RelNotes/2.0.2.txt32
-rw-r--r--Documentation/RelNotes/2.0.3.txt17
-rw-r--r--Documentation/RelNotes/2.1.0.txt114
-rw-r--r--Documentation/config.txt22
-rw-r--r--Documentation/git-replace.txt18
-rw-r--r--Documentation/git-rev-parse.txt6
-rw-r--r--Documentation/git-submodule.txt2
-rw-r--r--Documentation/git-tag.txt5
-rw-r--r--Documentation/git-update-index.txt11
-rw-r--r--Documentation/git-verify-commit.txt28
-rw-r--r--Documentation/git.txt66
-rw-r--r--Documentation/gitrepository-layout.txt4
-rw-r--r--Documentation/technical/api-hashmap.txt54
-rw-r--r--Documentation/technical/api-string-list.txt5
-rw-r--r--Documentation/technical/api-trace.txt97
-rw-r--r--Documentation/technical/http-protocol.txt2
-rw-r--r--Documentation/technical/index-format.txt35
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--INSTALL14
-rw-r--r--Makefile36
-rw-r--r--advice.c5
-rw-r--r--alias.c3
-rw-r--r--alloc.c106
-rw-r--r--blob.c11
-rw-r--r--branch.c4
-rw-r--r--builtin.h1
-rw-r--r--builtin/add.c6
-rw-r--r--builtin/annotate.c12
-rw-r--r--builtin/apply.c30
-rw-r--r--builtin/blame.c77
-rw-r--r--builtin/branch.c6
-rw-r--r--builtin/checkout-index.c4
-rw-r--r--builtin/checkout.c16
-rw-r--r--builtin/clean.c5
-rw-r--r--builtin/clone.c45
-rw-r--r--builtin/commit-tree.c4
-rw-r--r--builtin/commit.c47
-rw-r--r--builtin/describe.c13
-rw-r--r--builtin/diff-tree.c12
-rw-r--r--builtin/fast-export.c5
-rw-r--r--builtin/fetch.c9
-rw-r--r--builtin/fmt-merge-msg.c22
-rw-r--r--builtin/for-each-ref.c16
-rw-r--r--builtin/fsck.c8
-rw-r--r--builtin/index-pack.c35
-rw-r--r--builtin/log.c27
-rw-r--r--builtin/merge.c33
-rw-r--r--builtin/mv.c7
-rw-r--r--builtin/name-rev.c5
-rw-r--r--builtin/pack-objects.c4
-rw-r--r--builtin/push.c7
-rw-r--r--builtin/read-tree.c18
-rw-r--r--builtin/receive-pack.c7
-rw-r--r--builtin/remote.c22
-rw-r--r--builtin/repack.c21
-rw-r--r--builtin/replace.c171
-rw-r--r--builtin/reset.c11
-rw-r--r--builtin/rev-list.c5
-rw-r--r--builtin/rev-parse.c10
-rw-r--r--builtin/rm.c7
-rw-r--r--builtin/show-branch.c10
-rw-r--r--builtin/tag.c74
-rw-r--r--builtin/update-index.c33
-rw-r--r--builtin/update-ref.c20
-rw-r--r--builtin/verify-commit.c93
-rw-r--r--builtin/verify-pack.c7
-rw-r--r--bundle.c15
-rw-r--r--cache-tree.c52
-rw-r--r--cache-tree.h6
-rw-r--r--cache.h55
-rwxr-xr-xcheck_bindir2
-rw-r--r--column.c5
-rw-r--r--command-list.txt1
-rw-r--r--commit-slab.h12
-rw-r--r--commit.c264
-rw-r--r--commit.h62
-rw-r--r--compat/mingw.c400
-rw-r--r--compat/mingw.h147
-rw-r--r--compat/win32/dirent.c116
-rw-r--r--compat/win32/dirent.h8
-rw-r--r--compat/winansi.c450
-rw-r--r--config.c11
-rw-r--r--config.mak.uname3
-rw-r--r--connect.c15
-rw-r--r--connected.c6
-rw-r--r--contrib/completion/git-completion.bash10
-rwxr-xr-xcontrib/convert-grafts-to-replace-refs.sh28
-rwxr-xr-xcontrib/examples/git-clone.sh2
-rwxr-xr-xcontrib/examples/git-commit.sh4
-rwxr-xr-xcontrib/examples/git-merge.sh4
-rwxr-xr-xcontrib/examples/git-repack.sh4
-rwxr-xr-xcontrib/examples/git-resolve.sh2
-rw-r--r--contrib/subtree/Makefile8
-rw-r--r--convert.c4
-rw-r--r--credential-cache--daemon.c6
-rw-r--r--credential.c3
-rw-r--r--daemon.c99
-rw-r--r--decorate.c5
-rw-r--r--diff.c125
-rw-r--r--diffcore-rename.c11
-rw-r--r--dir.c62
-rw-r--r--dir.h44
-rw-r--r--entry.c3
-rw-r--r--environment.c22
-rw-r--r--ewah/ewah_io.c4
-rw-r--r--ewah/ewok.h3
-rw-r--r--fast-import.c227
-rw-r--r--fetch-pack.c24
-rw-r--r--fsck.c46
-rwxr-xr-xgit-bisect.sh2
-rw-r--r--git-compat-util.h85
-rwxr-xr-xgit-filter-branch.sh8
-rwxr-xr-xgit-gui/git-gui.sh6
-rw-r--r--git-gui/lib/diff.tcl3
-rwxr-xr-xgit-mergetool.sh4
-rwxr-xr-xgit-p4.py12
-rw-r--r--git-rebase--am.sh17
-rw-r--r--git-rebase--interactive.sh4
-rw-r--r--git-rebase--merge.sh5
-rwxr-xr-xgit-rebase.sh7
-rwxr-xr-xgit-submodule.sh46
-rw-r--r--git.c78
-rwxr-xr-xgitk-git/gitk70
-rw-r--r--gitk-git/po/vi.po1351
-rw-r--r--gpg-interface.c14
-rw-r--r--gpg-interface.h2
-rw-r--r--hashmap.c38
-rw-r--r--hashmap.h27
-rw-r--r--help.c27
-rw-r--r--http-backend.c15
-rw-r--r--http-push.c75
-rw-r--r--http-walker.c6
-rw-r--r--http.c7
-rw-r--r--imap-send.c6
-rw-r--r--khash.h11
-rw-r--r--line-log.c17
-rw-r--r--lockfile.c59
-rw-r--r--log-tree.c41
-rw-r--r--match-trees.c9
-rw-r--r--merge-recursive.c96
-rw-r--r--merge.c49
-rw-r--r--name-hash.c7
-rw-r--r--notes-cache.c6
-rw-r--r--notes-merge.c13
-rw-r--r--notes-utils.c7
-rw-r--r--notes-utils.h2
-rw-r--r--object.c43
-rw-r--r--object.h4
-rw-r--r--pack-objects.c5
-rw-r--r--parse-options.c16
-rw-r--r--path.c10
-rw-r--r--pathspec.c5
-rw-r--r--pkt-line.c8
-rw-r--r--po/git.pot2804
-rw-r--r--preload-index.c4
-rw-r--r--pretty.c68
-rw-r--r--prio-queue.c60
-rw-r--r--prio-queue.h8
-rw-r--r--progress.c71
-rw-r--r--read-cache.c295
-rw-r--r--refs.c422
-rw-r--r--refs.h55
-rw-r--r--remote-curl.c29
-rw-r--r--remote-testsvn.c16
-rw-r--r--remote.c11
-rw-r--r--rerere.c7
-rw-r--r--resolve-undo.c2
-rw-r--r--revision.c15
-rw-r--r--run-command.c15
-rw-r--r--sequencer.c76
-rw-r--r--sh-i18n--envsubst.c4
-rw-r--r--sha1_file.c103
-rw-r--r--sha1_name.c29
-rw-r--r--shallow.c10
-rw-r--r--shell.c6
-rw-r--r--split-index.c328
-rw-r--r--split-index.h35
-rw-r--r--strbuf.c28
-rw-r--r--strbuf.h18
-rw-r--r--string-list.c6
-rw-r--r--string-list.h2
-rw-r--r--submodule.c7
-rw-r--r--symlinks.c63
-rw-r--r--t/Makefile5
-rw-r--r--t/lib-httpd.sh2
-rwxr-xr-xt/lib-httpd/error.sh4
-rwxr-xr-xt/lib-submodule-update.sh680
-rwxr-xr-xt/perf/p5310-pack-bitmaps.sh3
-rwxr-xr-xt/t0001-init.sh4
-rwxr-xr-xt/t0008-ignores.sh14
-rwxr-xr-xt/t0011-hashmap.sh13
-rwxr-xr-xt/t0025-crlf-auto.sh122
-rwxr-xr-xt/t0026-eol-config.sh8
-rwxr-xr-xt/t0027-auto-crlf.sh265
-rwxr-xr-xt/t1013-read-tree-submodule.sh12
-rwxr-xr-xt/t1402-check-ref-format.sh15
-rwxr-xr-xt/t1700-split-index.sh194
-rwxr-xr-xt/t2013-checkout-submodule.sh5
-rwxr-xr-xt/t2104-update-index-skip-worktree.sh2
-rwxr-xr-xt/t3400-rebase.sh23
-rwxr-xr-xt/t3402-rebase-merge.sh15
-rwxr-xr-xt/t3426-rebase-submodule.sh46
-rwxr-xr-xt/t3512-cherry-pick-submodule.sh13
-rwxr-xr-xt/t3513-revert-submodule.sh32
-rwxr-xr-xt/t3906-stash-submodule.sh24
-rwxr-xr-xt/t4102-apply-rename.sh2
-rwxr-xr-xt/t4137-apply-submodule.sh20
-rwxr-xr-xt/t4202-log.sh31
-rwxr-xr-xt/t4255-am-submodule.sh21
-rwxr-xr-xt/t5000-tar-tree.sh14
-rwxr-xr-xt/t5003-archive-zip.sh12
-rwxr-xr-xt/t5310-pack-bitmaps.sh2
-rwxr-xr-xt/t5403-post-checkout-hook.sh8
-rwxr-xr-xt/t5539-fetch-http-shallow.sh1
-rwxr-xr-xt/t5550-http-fetch-dumb.sh5
-rwxr-xr-xt/t5572-pull-submodule.sh45
-rwxr-xr-xt/t6006-rev-list-format.sh6
-rwxr-xr-xt/t6023-merge-file.sh91
-rwxr-xr-xt/t6041-bisect-submodule.sh32
-rwxr-xr-xt/t6050-replace.sh139
-rwxr-xr-xt/t7003-filter-branch.sh11
-rwxr-xr-xt/t7004-tag.sh76
-rwxr-xr-xt/t7112-reset-submodule.sh14
-rwxr-xr-xt/t7300-clean.sh4
-rwxr-xr-xt/t7510-signed-commit.sh91
-rwxr-xr-xt/t7613-merge-submodule.sh19
-rwxr-xr-xt/t7700-repack.sh22
-rwxr-xr-xt/t7702-repack-cyclic-alternate.sh24
-rwxr-xr-xt/t9814-git-p4-rename.sh4
-rwxr-xr-xt/t9902-completion.sh27
-rw-r--r--t/test-lib-functions.sh15
-rw-r--r--t/test-lib.sh4
-rw-r--r--t/valgrind/default.supp15
-rw-r--r--tag.c11
-rw-r--r--test-dump-cache-tree.c7
-rw-r--r--test-dump-split-index.c34
-rw-r--r--test-hashmap.c25
-rw-r--r--test-scrap-cache-tree.c5
-rw-r--r--trace.c369
-rw-r--r--trace.h113
-rw-r--r--transport-helper.c14
-rw-r--r--transport.c36
-rw-r--r--tree-walk.c10
-rw-r--r--tree.c11
-rw-r--r--unicode_width.h49
-rw-r--r--unix-socket.c3
-rw-r--r--unpack-trees.c46
-rw-r--r--url.c2
-rw-r--r--urlmatch.c3
-rw-r--r--walker.c18
-rw-r--r--wt-status.c27
-rw-r--r--xdiff/xmerge.c4
256 files changed, 10256 insertions, 3881 deletions
diff --git a/.gitignore b/.gitignore
index 42294e59a1..81e12c0621 100644
--- a/.gitignore
+++ b/.gitignore
@@ -165,6 +165,7 @@
/git-upload-archive
/git-upload-pack
/git-var
+/git-verify-commit
/git-verify-pack
/git-verify-tag
/git-web--browse
@@ -180,6 +181,7 @@
/test-date
/test-delta
/test-dump-cache-tree
+/test-dump-split-index
/test-scrap-cache-tree
/test-genrandom
/test-hashmap
diff --git a/.mailmap b/.mailmap
index 11057cbcdf..8aefb5a452 100644
--- a/.mailmap
+++ b/.mailmap
@@ -85,6 +85,7 @@ Jeff King <peff@peff.net> <peff@github.com>
Jeff Muizelaar <jmuizelaar@mozilla.com> <jeff@infidigm.net>
Jens Axboe <axboe@kernel.dk> <axboe@suse.de>
Jens Axboe <axboe@kernel.dk> <jens.axboe@oracle.com>
+Jens Lindström <jl@opera.com> Jens Lindstrom <jl@opera.com>
Jim Meyering <jim@meyering.net> <meyering@redhat.com>
Joachim Berdal Haga <cjhaga@fys.uio.no>
Johannes Schindelin <Johannes.Schindelin@gmx.de> <johannes.schindelin@gmx.de>
@@ -113,6 +114,7 @@ Karsten Blees <blees@dcon.de> <karsten.blees@dcon.de>
Karsten Blees <blees@dcon.de> <karsten.blees@gmail.com>
Kay Sievers <kay.sievers@vrfy.org> <kay.sievers@suse.de>
Kay Sievers <kay.sievers@vrfy.org> <kay@mam.(none)>
+Kazuki Saitoh <ksaitoh560@gmail.com> kazuki saitoh <ksaitoh560@gmail.com>
Keith Cascio <keith@CS.UCLA.EDU> <keith@cs.ucla.edu>
Kent Engstrom <kent@lysator.liu.se>
Kevin Leung <kevinlsk@gmail.com>
@@ -202,6 +204,7 @@ Seth Falcon <seth@userprimary.net> <sfalcon@fhcrc.org>
Shawn O. Pearce <spearce@spearce.org>
Simon Hausmann <hausmann@kde.org> <simon@lst.de>
Simon Hausmann <hausmann@kde.org> <shausman@trolltech.com>
+Stefan Beller <stefanbeller@gmail.com> <stefanbeller@googlemail.com>
Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@atlas-elektronik.com>
Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@googlemail.com>
Stefan Sperling <stsp@elego.de> <stsp@stsp.name>
@@ -229,6 +232,7 @@ Tommi Virtanen <tv@debian.org> <tv@inoi.fi>
Tommy Thorn <tommy-git@thorn.ws> <tt1729@yahoo.com>
Tony Luck <tony.luck@intel.com>
Tor Arne Vestbø <torarnv@gmail.com> <tavestbo@trolltech.com>
+Trần Ngọc Quân <vnwildman@gmail.com> Tran Ngoc Quan <vnwildman@gmail.com>
Trent Piepho <tpiepho@gmail.com> <tpiepho@freescale.com>
Trent Piepho <tpiepho@gmail.com> <xyzzy@speakeasy.org>
Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <Uwe.Kleine-Koenig@digi.com>
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 4d90c77c7b..894546dd75 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -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
index 0000000000..ce5579db3e
--- /dev/null
+++ b/Documentation/RelNotes/2.0.1.txt
@@ -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
index 0000000000..8e8321b2ef
--- /dev/null
+++ b/Documentation/RelNotes/2.0.2.txt
@@ -0,0 +1,32 @@
+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.
+
+ * "git clone -b brefs/tags/bar" would have mistakenly thought we were
+ following a single tag, even though it was a name of the branch,
+ because it incorrectly used strstr().
+
+ * "%G" (nothing after G) is an invalid pretty format specifier, but
+ the parser did not notice it as garbage.
+
+ * Code to avoid adding the same alternate object store twice was
+ subtly broken for a long time, but nobody seems to have noticed.
+
+ * A handful of code paths had to read the commit object more than
+ once when showing header fields that are usually not parsed. The
+ internal data structure to keep track of the contents of the commit
+ object has been updated to reduce the need for this double-reading,
+ and to allow the caller find the length of the object.
+
+ * During "git rebase --merge", a conflicted patch could not be
+ skipped with "--skip" if the next one also conflicted.
diff --git a/Documentation/RelNotes/2.0.3.txt b/Documentation/RelNotes/2.0.3.txt
new file mode 100644
index 0000000000..4047b46bbe
--- /dev/null
+++ b/Documentation/RelNotes/2.0.3.txt
@@ -0,0 +1,17 @@
+Git v2.0.3 Release Notes
+========================
+
+ * An ancient rewrite passed a wrong pointer to a curl library
+ function in a rarely used code path.
+
+ * "filter-branch" left an empty single-parent commit that results when
+ all parents of a merge commit gets mapped to the same commit, even
+ under "--prune-empty".
+
+ * "log --show-signature" incorrectly decided the color to paint a
+ mergetag that was and was not correctly validated.
+
+ * "log --show-signature" did not pay attention to "--graph" option.
+
+Also a lot of fixes to the tests and some updates to the docs are
+included.
diff --git a/Documentation/RelNotes/2.1.0.txt b/Documentation/RelNotes/2.1.0.txt
index 2a003fd59d..be598ad7a2 100644
--- a/Documentation/RelNotes/2.1.0.txt
+++ b/Documentation/RelNotes/2.1.0.txt
@@ -37,15 +37,24 @@ 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.
+ * "git clone" applies the "if cloning from a local disk, physically
+ copy repository using hardlinks, unless otherwise told not to with
+ --no-local" optimization when url.*.insteadOf mechanism rewrites a
+ "git clone $URL" that refers to a repository over the network to a
+ clone from a local disk.
+
* "git commit --date=<date>" option learned to read from more
timestamp formats, including "--date=now".
@@ -90,7 +99,11 @@ UI, Workflows & Features
users need to explicitly set the variable to 'true' if they want
to resurrect the now-ignored use case.
- * "git replace" learned the "--edit" subcommand.
+ * "git replace" learned the "--edit" subcommand to create a
+ replacement by editing an existing object.
+
+ * "git replace" learned a "--graft" option to rewrite parents of a
+ commit.
* "git send-email" learned "--to-cover" and "--cc-cover" options, to
tell it to copy To: and Cc: headers found in the first input file
@@ -103,11 +116,39 @@ UI, Workflows & Features
* "git tag" when editing the tag message shows the name of the tag
being edited as a comment in the editor.
+ * "git tag" learned to pay attention to "tag.sort" configuration, to
+ be used as the default sort order when no --sort=<value> the option
+ is given.
+
+ * "git verify-commit" command to check GPG signature in signed
+ commits, in a way similar to "git verify-tag" is used to check
+ signed tags, was added.
+
Performance, Internal Implementation, etc.
* Build procedure for 'subtree' (in contrib/) has been cleaned up.
+ * The support for the profile-feedback build, which has been left
+ bit-rotten for quite a while, has been updated.
+
+ * An experimental format to use two files (the base file and
+ incremental changes relative to it) to represent the index has been
+ introduced; this may reduce I/O cost of rewriting a large index
+ when only small part of the working tree changes.
+
+ * Effort to shrink the size of patches Windows folks maintain on top
+ by upstreaming them continues.
+
+ * 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.
@@ -122,6 +163,9 @@ Performance, Internal Implementation, etc.
example, "update-ref --stdin [-z]" has been updated to use this
API.
+ * skip_prefix() and strip_suffix() API functions are used a lot more
+ widely throughout the codebase now.
+
* Parts of the test scripts can be skipped by using a range notation,
e.g. "sh t1234-test.sh --run='1-4 6 8-'" to omit test piece 5 and 7
and run everything else.
@@ -151,7 +195,7 @@ 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
@@ -163,6 +207,11 @@ notes for details).
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).
+ (merge 539e750 ek/alt-odb-entry-fix later to maint).
+
* The "%<(10,trunc)%s" pretty format specifier in the log family of
commands is used to truncate the string to a given length (e.g. 10
in the example) with padding to column-align the output, but did
@@ -170,6 +219,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).
@@ -179,6 +239,16 @@ notes for details).
couple of options unique to "git merge".
(merge 8fee872 jk/complete-merge-pull later to maint).
+ * The unix-domain socket used by the sample credential cache daemon
+ tried to unlink an existing stale one at a wrong path, if the path
+ to the socket was given as an overlong path that does not fit in
+ sun_path member of the sockaddr_un structure.
+ (merge 2869b3e rs/fix-unlink-unix-socket later to maint).
+
+ * An ancient rewrite passed a wrong pointer to a curl library
+ function in a rarely used code path.
+ (merge 479eaa8 ah/fix-http-push later to maint).
+
* "--ignore-space-change" option of "git apply" ignored the spaces
at the beginning of line too aggressively, which is inconsistent
with the option of the same name "diff" and "git diff" have.
@@ -194,6 +264,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).
@@ -203,6 +278,11 @@ notes for details).
bit.
(merge 5304810 jk/diff-files-assume-unchanged later to maint).
+ * "filter-branch" left an empty single-parent commit that results when
+ all parents of a merge commit gets mapped to the same commit, even
+ under "--prune-empty".
+ (merge 79bc4ef cb/filter-branch-prune-empty-degenerate-merges later to maint).
+
* "git format-patch" did not enforce the rule that the "--follow"
option from the log/diff family of commands must be used with
exactly one pathspec.
@@ -228,6 +308,13 @@ notes for details).
distinguish missing objects from type errors.
(merge 77583e7 jk/index-pack-report-missing later to maint).
+ * "log --show-signature" incorrectly decided the color to paint a
+ mergetag that was and was not correctly validated.
+ (merge 42c55ce mg/fix-log-mergetag-color later to maint).
+
+ * "log --show-signature" did not pay attention to "--graph" option.
+ (merge cf3983d zk/log-graph-showsig later to maint).
+
* "git mailinfo" used to read beyond the end of header string while
parsing an incoming e-mail message to extract the patch.
(merge b1a013d rs/mailinfo-header-cmp later to maint).
@@ -237,11 +324,21 @@ notes for details).
except for case differences.
(merge baa37bf dt/merge-recursive-case-insensitive later to maint).
+ * Merging changes into a file that ends in an incomplete line made the
+ last line into a complete one, even when the other branch did not
+ change anything around the end of file.
+ (merge ba31180 mk/merge-incomplete-files later to maint).
+
* "git pack-objects" unnecessarily copied the previous contents when
extending the hashtable, even though it will populate the table
from scratch anyway.
(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).
@@ -262,6 +359,13 @@ notes for details).
emptying the insn sheet.
(merge ddb5432 rr/rebase-autostash-fix later to maint).
+ * "git rebase --fork-point" did not filter out patch-identical
+ commits correctly.
+
+ * During "git rebase --merge", a conflicted patch could not be
+ skipped with "--skip" if the next one also conflicted.
+ (merge 95104c7 bc/fix-rebase-merge-skip later to maint).
+
* "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).
@@ -281,6 +385,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).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 9f467d3820..c55c22ab7b 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -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
@@ -2351,6 +2354,11 @@ submodule.<name>.ignore::
"--ignore-submodules" option. The 'git submodule' commands are not
affected by this setting.
+tag.sort::
+ This variable controls the sort ordering of tags when displayed by
+ linkgit:git-tag[1]. Without the "--sort=<value>" option provided, the
+ value of this variable will be used as the default.
+
tar.umask::
This variable can be used to restrict the permission bits of
tar archive entries. The default is 0002, which turns off the
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
index 61461b9f33..8fff598fd6 100644
--- a/Documentation/git-replace.txt
+++ b/Documentation/git-replace.txt
@@ -10,6 +10,7 @@ SYNOPSIS
[verse]
'git replace' [-f] <object> <replacement>
'git replace' [-f] --edit <object>
+'git replace' [-f] --graft <commit> [<parent>...]
'git replace' -d <object>...
'git replace' [--format=<format>] [-l [<pattern>]]
@@ -73,6 +74,23 @@ OPTIONS
newly created object. See linkgit:git-var[1] for details about
how the editor will be chosen.
+--raw::
+ When editing, provide the raw object contents rather than
+ pretty-printed ones. Currently this only affects trees, which
+ will be shown in their binary form. This is harder to work with,
+ but can help when repairing a tree that is so corrupted it
+ cannot be pretty-printed. Note that you may need to configure
+ your editor to cleanly read and write binary data.
+
+--graft <commit> [<parent>...]::
+ Create a graft commit. A new commit is created with the same
+ content as <commit> except that its parents will be
+ [<parent>...] instead of <commit>'s parents. A replacement ref
+ is then created to replace <commit> with the newly created
+ commit. See contrib/convert-grafts-to-replace-refs.sh for an
+ example script based on this option that can convert grafts to
+ replace refs.
+
-l <pattern>::
--list <pattern>::
List replace refs for objects that match the given pattern (or
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 987395d22a..0b84769bd9 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -102,7 +102,7 @@ eval "set -- $(git rev-parse --sq --prefix "$prefix" "$@")"
+
If you want to make sure that the output actually names an object in
your object database and/or can be used as a specific type of object
-you require, you can add "^{type}" peeling operator to the parameter.
+you require, you can add "\^{type}" peeling operator to the parameter.
For example, `git rev-parse "$VAR^{commit}"` will make sure `$VAR`
names an existing object that is a commit-ish (i.e. a commit, or an
annotated tag that points at a commit). To make sure that `$VAR`
@@ -245,6 +245,10 @@ print a message to stderr and exit with nonzero status.
--show-toplevel::
Show the absolute path of the top-level directory.
+--shared-index-path::
+ Show the path to the shared index file in split index mode, or
+ empty if not in split-index mode.
+
Other Options
~~~~~~~~~~~~~
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 89c4d3e394..8e6af65da0 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -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
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index b424a1bc48..320908369f 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -99,7 +99,9 @@ OPTIONS
Sort in a specific order. Supported type is "refname"
(lexicographic order), "version:refname" or "v:refname" (tag
names are treated as versions). Prepend "-" to reverse sort
- order.
+ order. When this option is not given, the sort order defaults to the
+ value configured for the 'tag.sort' variable if it exists, or
+ lexicographic order otherwise. See linkgit:git-config[1].
--column[=<options>]::
--no-column::
@@ -317,6 +319,7 @@ include::date-formats.txt[]
SEE ALSO
--------
linkgit:git-check-ref-format[1].
+linkgit:git-config[1].
GIT
---
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index d6de4a008c..dfc09d93d8 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -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
index 0000000000..9413e2802a
--- /dev/null
+++ b/Documentation/git-verify-commit.txt
@@ -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
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 3bd68b0167..a0f1ede866 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,9 +43,12 @@ 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.3/git.html[documentation for release 2.0.3]
* release notes for
+ link:RelNotes/2.0.3.txt[2.0.3],
+ link:RelNotes/2.0.2.txt[2.0.2],
+ link:RelNotes/2.0.1.txt[2.0.1],
link:RelNotes/2.0.0.txt[2.0.0].
* link:v1.9.4/git.html[documentation for release 1.9.4]
@@ -904,31 +907,54 @@ for further details.
based on whether stdout appears to be redirected to a file or not.
'GIT_TRACE'::
- If this variable is set to "1", "2" or "true" (comparison
- is case insensitive), Git will print `trace:` messages on
- stderr telling about alias expansion, built-in command
- execution and external command execution.
- If this variable is set to an integer value greater than 1
- and lower than 10 (strictly) then Git will interpret this
- value as an open file descriptor and will try to write the
- trace messages into this file descriptor.
- Alternatively, if this variable is set to an absolute path
- (starting with a '/' character), Git will interpret this
- as a file path and will try to write the trace messages
- into it.
+ Enables general trace messages, e.g. alias expansion, built-in
+ command execution and external command execution.
++
+If this variable is set to "1", "2" or "true" (comparison
+is case insensitive), trace messages will be printed to
+stderr.
++
+If the variable is set to an integer value greater than 2
+and lower than 10 (strictly) then Git will interpret this
+value as an open file descriptor and will try to write the
+trace messages into this file descriptor.
++
+Alternatively, if the variable is set to an absolute path
+(starting with a '/' character), Git will interpret this
+as a file path and will try to write the trace messages
+into it.
++
+Unsetting the variable, or setting it to empty, "0" or
+"false" (case insensitive) disables trace messages.
'GIT_TRACE_PACK_ACCESS'::
- If this variable is set to a path, a file will be created at
- the given path logging all accesses to any packs. For each
+ Enables trace messages for all accesses to any packs. For each
access, the pack file name and an offset in the pack is
recorded. This may be helpful for troubleshooting some
pack-related performance problems.
+ See 'GIT_TRACE' for available trace output options.
'GIT_TRACE_PACKET'::
- If this variable is set, it shows a trace of all packets
- coming in or out of a given program. This can help with
- debugging object negotiation or other protocol issues. Tracing
- is turned off at a packet starting with "PACK".
+ Enables trace messages for all packets coming in or out of a
+ given program. This can help with debugging object negotiation
+ or other protocol issues. Tracing is turned off at a packet
+ starting with "PACK".
+ See 'GIT_TRACE' for available trace output options.
+
+'GIT_TRACE_PERFORMANCE'::
+ Enables performance related trace messages, e.g. total execution
+ time of each Git command.
+ See 'GIT_TRACE' for available trace output options.
+
+'GIT_TRACE_SETUP'::
+ Enables trace messages printing the .git, working tree and current
+ working directory after Git has completed its setup phase.
+ See 'GIT_TRACE' for available trace output options.
+
+'GIT_TRACE_SHALLOW'::
+ Enables trace messages that can help debugging fetching /
+ cloning of shallow repositories.
+ See 'GIT_TRACE' for available trace output options.
GIT_LITERAL_PATHSPECS::
Setting this variable to `1` will cause Git to treat all
@@ -1042,7 +1068,7 @@ Authors
-------
Git was started by Linus Torvalds, and is currently maintained by Junio
C Hamano. Numerous contributions have come from the Git mailing list
-<git@vger.kernel.org>. http://www.ohloh.net/p/git/contributors/summary
+<git@vger.kernel.org>. http://www.openhub.net/p/git/contributors/summary
gives you a more complete list of contributors.
If you have a clone of git.git itself, the
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 17d2ea6c1e..79653f3134 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -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.
diff --git a/Documentation/technical/api-hashmap.txt b/Documentation/technical/api-hashmap.txt
index b977ae8bbb..ad7a5bddd2 100644
--- a/Documentation/technical/api-hashmap.txt
+++ b/Documentation/technical/api-hashmap.txt
@@ -8,11 +8,19 @@ Data Structures
`struct hashmap`::
- The hash table structure.
+ The hash table structure. Members can be used as follows, but should
+ not be modified directly:
+
-The `size` member keeps track of the total number of entries. The `cmpfn`
-member is a function used to compare two entries for equality. The `table` and
-`tablesize` members store the hash table and its size, respectively.
+The `size` member keeps track of the total number of entries (0 means the
+hashmap is empty).
++
+`tablesize` is the allocated size of the hash table. A non-0 value indicates
+that the hashmap is initialized. It may also be useful for statistical purposes
+(i.e. `size / tablesize` is the current load factor).
++
+`cmpfn` stores the comparison function specified in `hashmap_init()`. In
+advanced scenarios, it may be useful to change this, e.g. to switch between
+case-sensitive and case-insensitive lookup.
`struct hashmap_entry`::
@@ -58,6 +66,15 @@ Functions
+
`strihash` and `memihash` are case insensitive versions.
+`unsigned int sha1hash(const unsigned char *sha1)`::
+
+ Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
+ for use in hash tables. Cryptographic hashes are supposed to have
+ uniform distribution, so in contrast to `memhash()`, this just copies
+ the first `sizeof(int)` bytes without shuffling any bits. Note that
+ the results will be different on big-endian and little-endian
+ platforms, so they should not be stored or transferred over the net.
+
`void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function, size_t initial_size)`::
Initializes a hashmap structure.
@@ -101,6 +118,20 @@ hashmap_entry) that has at least been initialized with the proper hash code
If an entry with matching hash code is found, `key` and `keydata` are passed
to `hashmap_cmp_fn` to decide whether the entry matches the key.
+`void *hashmap_get_from_hash(const struct hashmap *map, unsigned int hash, const void *keydata)`::
+
+ Returns the hashmap entry for the specified hash code and key data,
+ or NULL if not found.
++
+`map` is the hashmap structure.
++
+`hash` is the hash code of the entry to look up.
++
+If an entry with matching hash code is found, `keydata` is passed to
+`hashmap_cmp_fn` to decide whether the entry matches the key. The
+`entry_or_key` parameter points to a bogus hashmap_entry structure that
+should not be used in the comparison.
+
`void *hashmap_get_next(const struct hashmap *map, const void *entry)`::
Returns the next equal hashmap entry, or NULL if not found. This can be
@@ -162,6 +193,21 @@ more entries.
`hashmap_iter_first` is a combination of both (i.e. initializes the iterator
and returns the first entry, if any).
+`const char *strintern(const char *string)`::
+`const void *memintern(const void *data, size_t len)`::
+
+ Returns the unique, interned version of the specified string or data,
+ similar to the `String.intern` API in Java and .NET, respectively.
+ Interned strings remain valid for the entire lifetime of the process.
++
+Can be used as `[x]strdup()` or `xmemdupz` replacement, except that interned
+strings / data must not be modified or freed.
++
+Interned strings are best used for short strings with high probability of
+duplicates.
++
+Uses a hashmap to store the pool of interned strings.
+
Usage example
-------------
diff --git a/Documentation/technical/api-string-list.txt b/Documentation/technical/api-string-list.txt
index f1add51efe..d51a6579c8 100644
--- a/Documentation/technical/api-string-list.txt
+++ b/Documentation/technical/api-string-list.txt
@@ -68,6 +68,11 @@ Functions
* General ones (works with sorted and unsorted lists as well)
+`string_list_init`::
+
+ Initialize the members of the string_list, set `strdup_strings`
+ member according to the value of the second parameter.
+
`filter_string_list`::
Apply a function to each item in a list, retaining only the
diff --git a/Documentation/technical/api-trace.txt b/Documentation/technical/api-trace.txt
new file mode 100644
index 0000000000..097a651d96
--- /dev/null
+++ b/Documentation/technical/api-trace.txt
@@ -0,0 +1,97 @@
+trace API
+=========
+
+The trace API can be used to print debug messages to stderr or a file. Trace
+code is inactive unless explicitly enabled by setting `GIT_TRACE*` environment
+variables.
+
+The trace implementation automatically adds `timestamp file:line ... \n` to
+all trace messages. E.g.:
+
+------------
+23:59:59.123456 git.c:312 trace: built-in: git 'foo'
+00:00:00.000001 builtin/foo.c:99 foo: some message
+------------
+
+Data Structures
+---------------
+
+`struct trace_key`::
+
+ Defines a trace key (or category). The default (for API functions that
+ don't take a key) is `GIT_TRACE`.
++
+E.g. to define a trace key controlled by environment variable `GIT_TRACE_FOO`:
++
+------------
+static struct trace_key trace_foo = TRACE_KEY_INIT(FOO);
+
+static void trace_print_foo(const char *message)
+{
+ trace_print_key(&trace_foo, message);
+}
+------------
++
+Note: don't use `const` as the trace implementation stores internal state in
+the `trace_key` structure.
+
+Functions
+---------
+
+`int trace_want(struct trace_key *key)`::
+
+ Checks whether the trace key is enabled. Used to prevent expensive
+ string formatting before calling one of the printing APIs.
+
+`void trace_disable(struct trace_key *key)`::
+
+ Disables tracing for the specified key, even if the environment
+ variable was set.
+
+`void trace_printf(const char *format, ...)`::
+`void trace_printf_key(struct trace_key *key, const char *format, ...)`::
+
+ Prints a formatted message, similar to printf.
+
+`void trace_argv_printf(const char **argv, const char *format, ...)``::
+
+ Prints a formatted message, followed by a quoted list of arguments.
+
+`void trace_strbuf(struct trace_key *key, const struct strbuf *data)`::
+
+ Prints the strbuf, without additional formatting (i.e. doesn't
+ choke on `%` or even `\0`).
+
+`uint64_t getnanotime(void)`::
+
+ Returns nanoseconds since the epoch (01/01/1970), typically used
+ for performance measurements.
++
+Currently there are high precision timer implementations for Linux (using
+`clock_gettime(CLOCK_MONOTONIC)`) and Windows (`QueryPerformanceCounter`).
+Other platforms use `gettimeofday` as time source.
+
+`void trace_performance(uint64_t nanos, const char *format, ...)`::
+`void trace_performance_since(uint64_t start, const char *format, ...)`::
+
+ Prints the elapsed time (in nanoseconds), or elapsed time since
+ `start`, followed by a formatted message. Enabled via environment
+ variable `GIT_TRACE_PERFORMANCE`. Used for manual profiling, e.g.:
++
+------------
+uint64_t start = getnanotime();
+/* code section to measure */
+trace_performance_since(start, "foobar");
+------------
++
+------------
+uint64_t t = 0;
+for (;;) {
+ /* ignore */
+ t -= getnanotime();
+ /* code section to measure */
+ t += getnanotime();
+ /* ignore */
+}
+trace_performance(t, "frotz");
+------------
diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt
index 59be59b0eb..229f845dfa 100644
--- a/Documentation/technical/http-protocol.txt
+++ b/Documentation/technical/http-protocol.txt
@@ -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.
diff --git a/Documentation/technical/index-format.txt b/Documentation/technical/index-format.txt
index f352a9b22e..fe6f31667d 100644
--- a/Documentation/technical/index-format.txt
+++ b/Documentation/technical/index-format.txt
@@ -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.
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 40adbf7bf7..f1bae1ce37 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.0.0.GIT
+DEF_VER=v2.1.0-rc0
LF='
'
diff --git a/INSTALL b/INSTALL
index ba01e7421e..6ec7a24e1a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -28,7 +28,7 @@ set up install paths (via config.mak.autogen), so you can write instead
If you're willing to trade off (much) longer build time for a later
faster git you can also do a profile feedback build with
- $ make prefix=/usr PROFILE=BUILD all
+ $ make prefix=/usr profile
# make prefix=/usr PROFILE=BUILD install
This will run the complete test suite as training workload and then
@@ -36,10 +36,20 @@ rebuild git with the generated profile feedback. This results in a git
which is a few percent faster on CPU intensive workloads. This
may be a good tradeoff for distribution packagers.
+Alternatively you can run profile feedback only with the git benchmark
+suite. This runs significantly faster than the full test suite, but
+has less coverage:
+
+ $ make prefix=/usr profile-fast
+ # make prefix=/usr PROFILE=BUILD install
+
Or if you just want to install a profile-optimized version of git into
your home directory, you could run:
- $ make PROFILE=BUILD install
+ $ make profile-install
+
+or
+ $ make profile-fast-install
As a caveat: a profile-optimized build takes a *lot* longer since the
git tree must be built twice, and in order for the profiling
diff --git a/Makefile b/Makefile
index 07ea105837..2320de592e 100644
--- a/Makefile
+++ b/Makefile
@@ -340,6 +340,8 @@ all::
#
# Define GMTIME_UNRELIABLE_ERRORS if your gmtime() function does not
# return NULL when it receives a bogus time_t.
+#
+# Define HAVE_CLOCK_GETTIME if your platform has clock_gettime in librt.
GIT-VERSION-FILE: FORCE
@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -552,6 +554,7 @@ TEST_PROGRAMS_NEED_X += test-ctype
TEST_PROGRAMS_NEED_X += test-date
TEST_PROGRAMS_NEED_X += test-delta
TEST_PROGRAMS_NEED_X += test-dump-cache-tree
+TEST_PROGRAMS_NEED_X += test-dump-split-index
TEST_PROGRAMS_NEED_X += test-genrandom
TEST_PROGRAMS_NEED_X += test-hashmap
TEST_PROGRAMS_NEED_X += test-index-version
@@ -875,6 +878,7 @@ LIB_OBJS += sha1_name.o
LIB_OBJS += shallow.o
LIB_OBJS += sideband.o
LIB_OBJS += sigchain.o
+LIB_OBJS += split-index.o
LIB_OBJS += strbuf.o
LIB_OBJS += streaming.o
LIB_OBJS += string-list.o
@@ -999,6 +1003,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
@@ -1497,6 +1502,11 @@ ifdef GMTIME_UNRELIABLE_ERRORS
BASIC_CFLAGS += -DGMTIME_UNRELIABLE_ERRORS
endif
+ifdef HAVE_CLOCK_GETTIME
+ BASIC_CFLAGS += -DHAVE_CLOCK_GETTIME
+ EXTLIBS += -lrt
+endif
+
ifeq ($(TCLTK_PATH),)
NO_TCLTK = NoThanks
endif
@@ -1552,13 +1562,13 @@ endif
PROFILE_DIR := $(CURDIR)
ifeq ("$(PROFILE)","GEN")
- CFLAGS += -fprofile-generate=$(PROFILE_DIR) -DNO_NORETURN=1
+ BASIC_CFLAGS += -fprofile-generate=$(PROFILE_DIR) -DNO_NORETURN=1
EXTLIBS += -lgcov
export CCACHE_DISABLE = t
V = 1
else
ifneq ("$(PROFILE)","")
- CFLAGS += -fprofile-use=$(PROFILE_DIR) -fprofile-correction -DNO_NORETURN=1
+ BASIC_CFLAGS += -fprofile-use=$(PROFILE_DIR) -fprofile-correction -DNO_NORETURN=1
export CCACHE_DISABLE = t
V = 1
endif
@@ -1643,12 +1653,20 @@ SHELL = $(SHELL_PATH)
all:: shell_compatibility_test
ifeq "$(PROFILE)" "BUILD"
-ifeq ($(filter all,$(MAKECMDGOALS)),all)
-all:: profile-clean
+all:: profile
+endif
+
+profile:: profile-clean
$(MAKE) PROFILE=GEN all
$(MAKE) PROFILE=GEN -j1 test
-endif
-endif
+ $(MAKE) PROFILE=GEN -j1 perf
+ $(MAKE) PROFILE=USE all
+
+profile-fast: profile-clean
+ $(MAKE) PROFILE=GEN all
+ $(MAKE) PROFILE=GEN -j1 perf
+ $(MAKE) PROFILE=USE all
+
all:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
ifneq (,$X)
@@ -2335,6 +2353,12 @@ mergetools_instdir_SQ = $(subst ','\'',$(mergetools_instdir))
install_bindir_programs := $(patsubst %,%$X,$(BINDIR_PROGRAMS_NEED_X)) $(BINDIR_PROGRAMS_NO_X)
+profile-install: profile
+ $(MAKE) install
+
+profile-fast-install: profile-fast
+ $(MAKE) install
+
install: all
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
diff --git a/advice.c b/advice.c
index c50ebdf5fe..9b42033192 100644
--- a/advice.c
+++ b/advice.c
@@ -61,9 +61,12 @@ void advise(const char *advice, ...)
int git_default_advice_config(const char *var, const char *value)
{
- const char *k = skip_prefix(var, "advice.");
+ const char *k;
int i;
+ if (!skip_prefix(var, "advice.", &k))
+ return 0;
+
for (i = 0; i < ARRAY_SIZE(advice_config); i++) {
if (strcmp(k, advice_config[i].name))
continue;
diff --git a/alias.c b/alias.c
index 5efc3d6986..758c867149 100644
--- a/alias.c
+++ b/alias.c
@@ -5,7 +5,8 @@ static char *alias_val;
static int alias_lookup_cb(const char *k, const char *v, void *cb)
{
- if (starts_with(k, "alias.") && !strcmp(k + 6, alias_key)) {
+ const char *name;
+ if (skip_prefix(k, "alias.", &name) && !strcmp(name, alias_key)) {
if (!v)
return config_error_nonbool(k);
alias_val = xstrdup(v);
diff --git a/alloc.c b/alloc.c
index f3ee745695..12afadfacd 100644
--- a/alloc.c
+++ b/alloc.c
@@ -18,25 +18,6 @@
#define BLOCKING 1024
-#define DEFINE_ALLOCATOR(name, type) \
-static unsigned int name##_allocs; \
-void *alloc_##name##_node(void) \
-{ \
- static int nr; \
- static type *block; \
- void *ret; \
- \
- if (!nr) { \
- nr = BLOCKING; \
- block = xmalloc(BLOCKING * sizeof(type)); \
- } \
- nr--; \
- name##_allocs++; \
- ret = block++; \
- memset(ret, 0, sizeof(type)); \
- return ret; \
-}
-
union any_object {
struct object object;
struct blob blob;
@@ -45,11 +26,75 @@ union any_object {
struct tag tag;
};
-DEFINE_ALLOCATOR(blob, struct blob)
-DEFINE_ALLOCATOR(tree, struct tree)
-DEFINE_ALLOCATOR(commit, struct commit)
-DEFINE_ALLOCATOR(tag, struct tag)
-DEFINE_ALLOCATOR(object, union any_object)
+struct alloc_state {
+ int count; /* total number of nodes allocated */
+ int nr; /* number of nodes left in current allocation */
+ void *p; /* first free node in current allocation */
+};
+
+static inline void *alloc_node(struct alloc_state *s, size_t node_size)
+{
+ void *ret;
+
+ if (!s->nr) {
+ s->nr = BLOCKING;
+ s->p = xmalloc(BLOCKING * node_size);
+ }
+ s->nr--;
+ s->count++;
+ ret = s->p;
+ s->p = (char *)s->p + node_size;
+ memset(ret, 0, node_size);
+ return ret;
+}
+
+static struct alloc_state blob_state;
+void *alloc_blob_node(void)
+{
+ struct blob *b = alloc_node(&blob_state, sizeof(struct blob));
+ b->object.type = OBJ_BLOB;
+ return b;
+}
+
+static struct alloc_state tree_state;
+void *alloc_tree_node(void)
+{
+ struct tree *t = alloc_node(&tree_state, sizeof(struct tree));
+ t->object.type = OBJ_TREE;
+ return t;
+}
+
+static struct alloc_state tag_state;
+void *alloc_tag_node(void)
+{
+ struct tag *t = alloc_node(&tag_state, sizeof(struct tag));
+ t->object.type = OBJ_TAG;
+ return t;
+}
+
+static struct alloc_state object_state;
+void *alloc_object_node(void)
+{
+ struct object *obj = alloc_node(&object_state, sizeof(union any_object));
+ obj->type = OBJ_NONE;
+ return obj;
+}
+
+static struct alloc_state commit_state;
+
+unsigned int alloc_commit_index(void)
+{
+ static unsigned int count;
+ return count++;
+}
+
+void *alloc_commit_node(void)
+{
+ struct commit *c = alloc_node(&commit_state, sizeof(struct commit));
+ c->object.type = OBJ_COMMIT;
+ c->index = alloc_commit_index();
+ return c;
+}
static void report(const char *name, unsigned int count, size_t size)
{
@@ -57,13 +102,14 @@ static void report(const char *name, unsigned int count, size_t size)
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##_state.count, name##_state.count * sizeof(type) >> 10)
void alloc_report(void)
{
- REPORT(blob);
- REPORT(tree);
- REPORT(commit);
- REPORT(tag);
+ REPORT(blob, struct blob);
+ REPORT(tree, struct tree);
+ REPORT(commit, struct commit);
+ REPORT(tag, struct tag);
+ REPORT(object, union any_object);
}
diff --git a/blob.c b/blob.c
index ae320bd8fa..1fcb8e44b0 100644
--- a/blob.c
+++ b/blob.c
@@ -7,15 +7,8 @@ struct blob *lookup_blob(const unsigned char *sha1)
{
struct object *obj = lookup_object(sha1);
if (!obj)
- return create_object(sha1, OBJ_BLOB, alloc_blob_node());
- if (!obj->type)
- obj->type = OBJ_BLOB;
- if (obj->type != OBJ_BLOB) {
- error("Object %s is a %s, not a blob",
- sha1_to_hex(sha1), typename(obj->type));
- return NULL;
- }
- return (struct blob *) obj;
+ return create_object(sha1, alloc_blob_node());
+ return object_as_type(obj, OBJ_BLOB, 0);
}
int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
diff --git a/branch.c b/branch.c
index 660097bc29..46e8aa86df 100644
--- a/branch.c
+++ b/branch.c
@@ -50,11 +50,11 @@ static int should_setup_rebase(const char *origin)
void install_branch_config(int flag, const char *local, const char *origin, const char *remote)
{
- const char *shortname = skip_prefix(remote, "refs/heads/");
+ const char *shortname = NULL;
struct strbuf key = STRBUF_INIT;
int rebasing = should_setup_rebase(origin);
- if (shortname
+ if (skip_prefix(remote, "refs/heads/", &shortname)
&& !strcmp(local, shortname)
&& !origin) {
warning(_("Not setting branch %s as its own upstream."),
diff --git a/builtin.h b/builtin.h
index c47c110e0f..5d91f31ca2 100644
--- 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);
diff --git a/builtin/add.c b/builtin/add.c
index 459208a326..4baf3a5635 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -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"));
}
diff --git a/builtin/annotate.c b/builtin/annotate.c
index fc43eed36b..da413ae0d1 100644
--- a/builtin/annotate.c
+++ b/builtin/annotate.c
@@ -5,20 +5,18 @@
*/
#include "git-compat-util.h"
#include "builtin.h"
+#include "argv-array.h"
int cmd_annotate(int argc, const char **argv, const char *prefix)
{
- const char **nargv;
+ struct argv_array args = ARGV_ARRAY_INIT;
int i;
- nargv = xmalloc(sizeof(char *) * (argc + 2));
- nargv[0] = "annotate";
- nargv[1] = "-c";
+ argv_array_pushl(&args, "annotate", "-c", NULL);
for (i = 1; i < argc; i++) {
- nargv[i+1] = argv[i];
+ argv_array_push(&args, argv[i]);
}
- nargv[argc + 1] = NULL;
- return cmd_blame(argc + 1, nargv, prefix);
+ return cmd_blame(args.argc, args.argv, prefix);
}
diff --git a/builtin/apply.c b/builtin/apply.c
index 9c5724eacc..9f8f5bac07 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -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;
}
@@ -2869,9 +2867,7 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
case BINARY_LITERAL_DEFLATED:
clear_image(img);
img->len = fragment->size;
- img->buf = xmalloc(img->len+1);
- memcpy(img->buf, fragment->patch, img->len);
- img->buf[img->len] = '\0';
+ img->buf = xmemdupz(fragment->patch, img->len);
return 0;
}
return -1;
@@ -3086,13 +3082,15 @@ static void prepare_fn_table(struct patch *patch)
}
}
-static int checkout_target(struct cache_entry *ce, struct stat *st)
+static int checkout_target(struct index_state *istate,
+ struct cache_entry *ce, struct stat *st)
{
struct checkout costate;
memset(&costate, 0, sizeof(costate));
costate.base_dir = "";
costate.refresh_cache = 1;
+ costate.istate = istate;
if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
return error(_("cannot checkout %s"), ce->name);
return 0;
@@ -3259,7 +3257,7 @@ static int load_current(struct image *image, struct patch *patch)
if (lstat(name, &st)) {
if (errno != ENOENT)
return error(_("%s: %s"), name, strerror(errno));
- if (checkout_target(ce, &st))
+ if (checkout_target(&the_index, ce, &st))
return -1;
}
if (verify_index_match(ce, &st))
@@ -3413,7 +3411,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
}
*ce = active_cache[pos];
if (stat_ret < 0) {
- if (checkout_target(*ce, st))
+ if (checkout_target(&the_index, *ce, st))
return -1;
}
if (!cached && verify_index_match(*ce, st))
@@ -3646,7 +3644,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
{
struct patch *patch;
struct index_state result = { NULL };
- int fd;
+ static struct lock_file lock;
/* Once we start supporting the reverse patch, it may be
* worth showing the new sha1 prefix, but until then...
@@ -3684,8 +3682,8 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
die ("Could not add %s to temporary index", name);
}
- fd = open(filename, O_WRONLY | O_CREAT, 0666);
- if (fd < 0 || write_index(&result, fd) || close(fd))
+ hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+ if (write_locked_index(&result, &lock, COMMIT_LOCK))
die ("Could not write temporary index to %s", filename);
discard_index(&result);
@@ -3847,9 +3845,10 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
ce->ce_flags = create_ce_flags(0);
ce->ce_namelen = namelen;
if (S_ISGITLINK(mode)) {
- const char *s = buf;
+ const char *s;
- if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
+ if (!skip_prefix(buf, "Subproject commit ", &s) ||
+ get_sha1_hex(s, ce->sha1))
die(_("corrupt patch for submodule %s"), path);
} else {
if (!cached) {
@@ -4503,8 +4502,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
}
if (update_index) {
- if (write_cache(newfd, active_cache, active_nr) ||
- commit_locked_index(&lock_file))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("Unable to write new index file"));
}
diff --git a/builtin/blame.c b/builtin/blame.c
index a52a279144..17d30d00aa 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1371,11 +1371,8 @@ static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit
static int num_scapegoats(struct rev_info *revs, struct commit *commit)
{
- int cnt;
struct commit_list *l = first_scapegoat(revs, commit);
- for (cnt = 0; l; l = l->next)
- cnt++;
- return cnt;
+ return commit_list_count(l);
}
/* Distribute collected unsorted blames to the respected sorted lists
@@ -1655,7 +1652,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 +1663,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 +1677,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 +2005,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 +2022,19 @@ static int prepare_lines(struct scoreboard *sb)
const char *end = buf + len;
const char *p;
int *lineno;
- int num = 0, incomplete = 0;
-
- for (p = buf;;) {
- p = memchr(p, '\n', end - p);
- if (p) {
- p++;
- num++;
- continue;
- }
- break;
- }
+ int num = 0;
- if (len && end[-1] != '\n')
- incomplete++; /* incomplete line at the end */
+ for (p = buf; p < end; p = get_next_line(p, end))
+ num++;
- sb->lineno = xmalloc(sizeof(*sb->lineno) * (num + incomplete + 1));
- lineno = sb->lineno;
+ sb->lineno = lineno = xmalloc(sizeof(*sb->lineno) * (num + 1));
- *lineno++ = 0;
- for (p = buf;;) {
- p = memchr(p, '\n', end - p);
- if (p) {
- p++;
- *lineno++ = p - buf;
- continue;
- }
- break;
- }
+ for (p = buf; p < end; p = get_next_line(p, end))
+ *lineno++ = p - buf;
- if (incomplete)
- *lineno++ = len;
+ *lineno = len;
- sb->num_lines = num + incomplete;
+ sb->num_lines = num;
return sb->num_lines;
}
@@ -2266,6 +2249,18 @@ static void append_merge_parents(struct commit_list **tail)
}
/*
+ * 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,10 +2281,9 @@ 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;
parent_tail = &commit->parents;
if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
@@ -2313,7 +2307,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 +2385,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
* right now, but someday we might optimize diff-index --cached
* with cache-tree information.
*/
- cache_tree_invalidate_path(active_cache_tree, path);
+ cache_tree_invalidate_path(&the_index, path);
return commit;
}
@@ -2709,11 +2703,8 @@ parse_done:
die("revision walk setup failed");
if (is_null_sha1(sb.final->object.sha1)) {
- char *buf;
o = sb.final->util;
- buf = xmalloc(o->file.size + 1);
- memcpy(buf, o->file.ptr, o->file.size + 1);
- sb.final_buf = buf;
+ sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
sb.final_buf_size = o->file.size;
}
else {
diff --git a/builtin/branch.c b/builtin/branch.c
index 652b1d2d14..0591b22a48 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -294,13 +294,13 @@ static char *resolve_symref(const char *src, const char *prefix)
{
unsigned char sha1[20];
int flag;
- const char *dst, *cp;
+ const char *dst;
dst = resolve_ref_unsafe(src, sha1, 0, &flag);
if (!(dst && (flag & REF_ISSYMREF)))
return NULL;
- if (prefix && (cp = skip_prefix(dst, prefix)))
- dst = cp;
+ if (prefix)
+ skip_prefix(dst, prefix, &dst);
return xstrdup(dst);
}
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 61e75eb60c..05edd9e1df 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -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;
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f1dc56e55f..f71e74531d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -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)
@@ -776,8 +774,8 @@ static int switch_branches(const struct checkout_opts *opts,
if (!(flag & REF_ISSYMREF))
old.path = NULL;
- if (old.path && starts_with(old.path, "refs/heads/"))
- old.name = old.path + strlen("refs/heads/");
+ if (old.path)
+ skip_prefix(old.path, "refs/heads/", &old.name);
if (!new->name) {
new->name = "HEAD";
diff --git a/builtin/clean.c b/builtin/clean.c
index 9a9151575d..1032563e5f 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -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
@@ -621,8 +621,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
nr += chosen[i];
}
- result = xmalloc(sizeof(int) * (nr + 1));
- memset(result, 0, sizeof(int) * (nr + 1));
+ result = xcalloc(nr + 1, sizeof(int));
for (i = 0; i < stuff->nr && j < nr; i++) {
if (chosen[i])
result[j++] = i;
diff --git a/builtin/clone.c b/builtin/clone.c
index b12989d1ca..bbd169ceb4 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -584,11 +584,11 @@ static void update_remote_refs(const struct ref *refs,
static void update_head(const struct ref *our, const struct ref *remote,
const char *msg)
{
- if (our && starts_with(our->name, "refs/heads/")) {
+ const char *head;
+ if (our && skip_prefix(our->name, "refs/heads/", &head)) {
/* Local default branch link */
create_symref("HEAD", our->name, NULL);
if (!option_bare) {
- const char *head = skip_prefix(our->name, "refs/heads/");
update_ref(msg, "HEAD", our->old_sha1, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
install_branch_config(0, head, option_origin, our->name);
@@ -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,16 +695,19 @@ 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
strbuf_addf(&value, "+%s:%s%s", our_head_points_at->name,
branch_top->buf, option_branch);
} else if (remote_head_points_at) {
+ const char *head = remote_head_points_at->name;
+ if (!skip_prefix(head, "refs/heads/", &head))
+ die("BUG: remote HEAD points at non-head?");
+
strbuf_addf(&value, "+%s:%s%s", remote_head_points_at->name,
- branch_top->buf,
- skip_prefix(remote_head_points_at->name, "refs/heads/"));
+ branch_top->buf, head);
}
/*
* otherwise, the next "git fetch" will
@@ -797,18 +799,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
die(_("repository '%s' does not exist"), repo_name);
else
repo = repo_name;
- is_local = option_local != 0 && path && !is_bundle;
- if (is_local) {
- if (option_depth)
- warning(_("--depth is ignored in local clones; use file:// instead."));
- if (!access(mkpath("%s/shallow", path), F_OK)) {
- if (option_local > 0)
- warning(_("source repository is shallow, ignoring --local"));
- is_local = 0;
- }
- }
- if (option_local > 0 && !is_local)
- warning(_("--local is ignored"));
/* no need to be strict, transport_set_option() will validate it again */
if (option_depth && atoi(option_depth) < 1)
@@ -901,6 +891,19 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote = remote_get(option_origin);
transport = transport_get(remote, remote->url[0]);
+ path = get_repo_path(remote->url[0], &is_bundle);
+ is_local = option_local != 0 && path && !is_bundle;
+ if (is_local) {
+ if (option_depth)
+ warning(_("--depth is ignored in local clones; use file:// instead."));
+ if (!access(mkpath("%s/shallow", path), F_OK)) {
+ if (option_local > 0)
+ warning(_("source repository is shallow, ignoring --local"));
+ is_local = 0;
+ }
+ }
+ if (option_local > 0 && !is_local)
+ warning(_("--local is ignored"));
transport->cloning = 1;
if (!transport->get_refs_list || (!is_local && !transport->fetch))
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 987a4c3d73..8a66c74e0f 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -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;
}
diff --git a/builtin/commit.c b/builtin/commit.c
index 5e2221c8e8..5ed60364ce 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -305,7 +305,6 @@ static void refresh_cache_or_die(int refresh_flags)
static char *prepare_index(int argc, const char **argv, const char *prefix,
const struct commit *current_head, int is_status)
{
- int fd;
struct string_list partial;
struct pathspec pathspec;
int refresh_flags = REFRESH_QUIET;
@@ -321,12 +320,11 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
if (interactive) {
char *old_index_env = NULL;
- fd = hold_locked_index(&index_lock, 1);
+ hold_locked_index(&index_lock, 1);
refresh_cache_or_die(refresh_flags);
- if (write_cache(fd, active_cache, active_nr) ||
- close_lock_file(&index_lock))
+ if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
die(_("unable to create temporary index"));
old_index_env = getenv(INDEX_ENVIRONMENT);
@@ -360,12 +358,11 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
* (B) on failure, rollback the real index.
*/
if (all || (also && pathspec.nr)) {
- fd = hold_locked_index(&index_lock, 1);
+ hold_locked_index(&index_lock, 1);
add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
refresh_cache_or_die(refresh_flags);
update_main_cache_tree(WRITE_TREE_SILENT);
- if (write_cache(fd, active_cache, active_nr) ||
- close_lock_file(&index_lock))
+ if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
die(_("unable to write new_index file"));
commit_style = COMMIT_NORMAL;
return index_lock.filename;
@@ -381,12 +378,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
* We still need to refresh the index here.
*/
if (!only && !pathspec.nr) {
- fd = hold_locked_index(&index_lock, 1);
+ hold_locked_index(&index_lock, 1);
refresh_cache_or_die(refresh_flags);
if (active_cache_changed) {
update_main_cache_tree(WRITE_TREE_SILENT);
- if (write_cache(fd, active_cache, active_nr) ||
- commit_locked_index(&index_lock))
+ if (write_locked_index(&the_index, &index_lock,
+ COMMIT_LOCK))
die(_("unable to write new_index file"));
} else {
rollback_lock_file(&index_lock);
@@ -423,8 +420,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
die(_("cannot do a partial commit during a cherry-pick."));
}
- memset(&partial, 0, sizeof(partial));
- partial.strdup_strings = 1;
+ string_list_init(&partial, 1);
if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
exit(1);
@@ -432,24 +428,22 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
if (read_cache() < 0)
die(_("cannot read the index"));
- fd = hold_locked_index(&index_lock, 1);
+ hold_locked_index(&index_lock, 1);
add_remove_files(&partial);
refresh_cache(REFRESH_QUIET);
- if (write_cache(fd, active_cache, active_nr) ||
- close_lock_file(&index_lock))
+ if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
die(_("unable to write new_index file"));
- fd = hold_lock_file_for_update(&false_lock,
- git_path("next-index-%"PRIuMAX,
- (uintmax_t) getpid()),
- LOCK_DIE_ON_ERROR);
+ hold_lock_file_for_update(&false_lock,
+ git_path("next-index-%"PRIuMAX,
+ (uintmax_t) getpid()),
+ LOCK_DIE_ON_ERROR);
create_base_index(current_head);
add_remove_files(&partial);
refresh_cache(REFRESH_QUIET);
- if (write_cache(fd, active_cache, active_nr) ||
- close_lock_file(&false_lock))
+ if (write_locked_index(&the_index, &false_lock, CLOSE_LOCK))
die(_("unable to write temporary index file"));
discard_cache();
@@ -707,7 +701,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
char *buffer;
buffer = strstr(use_message_buffer, "\n\n");
if (buffer)
- strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+ strbuf_addstr(&sb, buffer + 2);
hook_arg1 = "commit";
hook_arg2 = use_message;
} else if (fixup_message) {
@@ -1020,7 +1014,7 @@ static int message_is_empty(struct strbuf *sb)
static int template_untouched(struct strbuf *sb)
{
struct strbuf tmpl = STRBUF_INIT;
- char *start;
+ const char *start;
if (cleanup_mode == CLEANUP_NONE && sb->len)
return 0;
@@ -1029,8 +1023,7 @@ static int template_untouched(struct strbuf *sb)
return 0;
stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
- start = (char *)skip_prefix(sb->buf, tmpl.buf);
- if (!start)
+ if (!skip_prefix(sb->buf, tmpl.buf, &start))
start = sb->buf;
strbuf_release(&tmpl);
return rest_is_empty(sb, start - sb->buf);
@@ -1745,8 +1738,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"));
}
diff --git a/builtin/describe.c b/builtin/describe.c
index 24d740c8b1..ee6a3b998f 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -56,18 +56,9 @@ static int commit_name_cmp(const struct commit_name *cn1,
return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled);
}
-static inline unsigned int hash_sha1(const unsigned char *sha1)
-{
- unsigned int hash;
- memcpy(&hash, sha1, sizeof(hash));
- return hash;
-}
-
static inline struct commit_name *find_commit_name(const unsigned char *peeled)
{
- struct commit_name key;
- hashmap_entry_init(&key, hash_sha1(peeled));
- return hashmap_get(&names, &key, peeled);
+ return hashmap_get_from_hash(&names, sha1hash(peeled), peeled);
}
static int replace_name(struct commit_name *e,
@@ -114,7 +105,7 @@ static void add_to_known_names(const char *path,
if (!e) {
e = xmalloc(sizeof(struct commit_name));
hashcpy(e->peeled, peeled);
- hashmap_entry_init(e, hash_sha1(peeled));
+ hashmap_entry_init(e, sha1hash(peeled));
hashmap_add(&names, e);
e->path = NULL;
}
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index be6417d166..1c4ad6223e 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -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)) {
@@ -72,9 +68,7 @@ static int diff_tree_stdin(char *line)
line[len-1] = 0;
if (get_sha1_hex(line, sha1))
return -1;
- obj = lookup_unknown_object(sha1);
- if (!obj || !obj->parsed)
- obj = parse_object(sha1);
+ obj = parse_object(sha1);
if (!obj)
return -1;
if (obj->type == OBJ_COMMIT)
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index ef4481615f..92b4624a4b 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -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);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index dd46b61d9a..e8d0cca3e4 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1082,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];
}
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 3906eda877..79df05ef52 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -100,7 +100,8 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
{
int i, len = strlen(line);
struct origin_data *origin_data;
- char *src, *origin;
+ char *src;
+ const char *origin;
struct src_data *src_data;
struct string_list_item *item;
int pulling_head = 0;
@@ -164,8 +165,7 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
origin = line;
string_list_append(&src_data->tag, origin + 4);
src_data->head_status |= 2;
- } else if (starts_with(line, "remote-tracking branch ")) {
- origin = line + strlen("remote-tracking branch ");
+ } else if (skip_prefix(line, "remote-tracking branch ", &origin)) {
string_list_append(&src_data->r_branch, origin);
src_data->head_status |= 2;
} else {
@@ -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) {
@@ -297,8 +297,8 @@ static void credit_people(struct strbuf *out,
if (!them->nr ||
(them->nr == 1 &&
me &&
- (me = skip_prefix(me, them->items->string)) != NULL &&
- skip_prefix(me, " <")))
+ skip_prefix(me, them->items->string, &me) &&
+ starts_with(me, " <")))
return;
strbuf_addf(out, "\n%c %s ", comment_line_char, label);
add_people_count(out, them);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 4135980f20..47bd624696 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -283,18 +283,6 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
}
}
-static int num_parents(struct commit *commit)
-{
- struct commit_list *parents;
- int i;
-
- for (i = 0, parents = commit->parents;
- parents;
- parents = parents->next)
- i++;
- return i;
-}
-
/* See grab_values */
static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
{
@@ -315,12 +303,12 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
}
if (!strcmp(name, "numparent")) {
char *s = xmalloc(40);
- v->ul = num_parents(commit);
+ v->ul = commit_list_count(commit->parents);
sprintf(s, "%lu", v->ul);
v->s = s;
}
else if (!strcmp(name, "parent")) {
- int num = num_parents(commit);
+ int num = commit_list_count(commit->parents);
int i;
struct commit_list *parents;
char *s = xmalloc(41 * num + 1);
diff --git a/builtin/fsck.c b/builtin/fsck.c
index fc150c8821..d42a27da89 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -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));
@@ -482,11 +481,6 @@ static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, in
return 0;
}
-static int is_branch(const char *refname)
-{
- return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
-}
-
static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *obj;
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 18f57de58b..5568a5bc3b 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -362,8 +362,7 @@ static void set_thread_data(struct thread_local *data)
static struct base_data *alloc_base_data(void)
{
- struct base_data *base = xmalloc(sizeof(struct base_data));
- memset(base, 0, sizeof(*base));
+ struct base_data *base = xcalloc(1, sizeof(struct base_data));
base->ref_last = -1;
base->ofs_last = -1;
return base;
@@ -786,7 +785,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 +1505,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
const char *curr_index;
const char *index_name = NULL, *pack_name = NULL;
const char *keep_name = NULL, *keep_msg = NULL;
- char *index_name_buf = NULL, *keep_name_buf = NULL;
+ struct strbuf index_name_buf = STRBUF_INIT,
+ keep_name_buf = STRBUF_INIT;
struct pack_idx_entry **idx_objects;
struct pack_idx_option opts;
unsigned char pack_sha1[20];
@@ -1602,24 +1603,22 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (fix_thin_pack && !from_stdin)
die(_("--fix-thin cannot be used without --stdin"));
if (!index_name && pack_name) {
- int len = strlen(pack_name);
- if (!has_extension(pack_name, ".pack"))
+ size_t len;
+ if (!strip_suffix(pack_name, ".pack", &len))
die(_("packfile name '%s' does not end with '.pack'"),
pack_name);
- index_name_buf = xmalloc(len);
- memcpy(index_name_buf, pack_name, len - 5);
- strcpy(index_name_buf + len - 5, ".idx");
- index_name = index_name_buf;
+ strbuf_add(&index_name_buf, pack_name, len);
+ strbuf_addstr(&index_name_buf, ".idx");
+ index_name = index_name_buf.buf;
}
if (keep_msg && !keep_name && pack_name) {
- int len = strlen(pack_name);
- if (!has_extension(pack_name, ".pack"))
+ size_t len;
+ if (!strip_suffix(pack_name, ".pack", &len))
die(_("packfile name '%s' does not end with '.pack'"),
pack_name);
- keep_name_buf = xmalloc(len);
- memcpy(keep_name_buf, pack_name, len - 5);
- strcpy(keep_name_buf + len - 5, ".keep");
- keep_name = keep_name_buf;
+ strbuf_add(&keep_name_buf, pack_name, len);
+ strbuf_addstr(&keep_name_buf, ".idx");
+ keep_name = keep_name_buf.buf;
}
if (verify) {
if (!index_name)
@@ -1667,8 +1666,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
else
close(input_fd);
free(objects);
- free(index_name_buf);
- free(keep_name_buf);
+ strbuf_release(&index_name_buf);
+ strbuf_release(&keep_name_buf);
if (pack_name == NULL)
free((void *) curr_pack);
if (index_name == NULL)
diff --git a/builtin/log.c b/builtin/log.c
index a7ba211731..4389722b4b 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -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;
@@ -862,7 +861,7 @@ static void add_branch_description(struct strbuf *buf, const char *branch_name)
read_branch_desc(&desc, branch_name);
if (desc.len) {
strbuf_addch(buf, '\n');
- strbuf_add(buf, desc.buf, desc.len);
+ strbuf_addbuf(buf, &desc);
strbuf_addch(buf, '\n');
}
}
@@ -872,7 +871,7 @@ static char *find_branch_name(struct rev_info *rev)
int i, positive = -1;
unsigned char branch_sha1[20];
const unsigned char *tip_sha1;
- const char *ref;
+ const char *ref, *v;
char *full_ref, *branch = NULL;
for (i = 0; i < rev->cmdline.nr; i++) {
@@ -888,9 +887,9 @@ static char *find_branch_name(struct rev_info *rev)
ref = rev->cmdline.rev[positive].name;
tip_sha1 = rev->cmdline.rev[positive].item->sha1;
if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
- starts_with(full_ref, "refs/heads/") &&
+ skip_prefix(full_ref, "refs/heads/", &v) &&
!hashcmp(tip_sha1, branch_sha1))
- branch = xstrdup(full_ref + strlen("refs/heads/"));
+ branch = xstrdup(v);
free(full_ref);
return branch;
}
@@ -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);
@@ -1394,10 +1396,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (check_head) {
unsigned char sha1[20];
- const char *ref;
+ const char *ref, *v;
ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
- if (ref && starts_with(ref, "refs/heads/"))
- branch_name = xstrdup(ref + strlen("refs/heads/"));
+ if (ref && skip_prefix(ref, "refs/heads/", &v))
+ branch_name = xstrdup(v);
else
branch_name = xstrdup(""); /* no branch */
}
@@ -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
diff --git a/builtin/merge.c b/builtin/merge.c
index 428ca247bd..ce82eb297d 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -657,14 +657,12 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
struct commit_list *remoteheads,
struct commit *head, const char *head_arg)
{
- int index_fd;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
- index_fd = hold_locked_index(lock, 1);
+ hold_locked_index(lock, 1);
refresh_cache(REFRESH_QUIET);
if (active_cache_changed &&
- (write_cache(index_fd, active_cache, active_nr) ||
- commit_locked_index(lock)))
+ write_locked_index(&the_index, lock, COMMIT_LOCK))
return error(_("Unable to write index."));
rollback_lock_file(lock);
@@ -672,7 +670,6 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
int clean, x;
struct commit *result;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
- int index_fd;
struct commit_list *reversed = NULL;
struct merge_options o;
struct commit_list *j;
@@ -700,12 +697,11 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
for (j = common; j; j = j->next)
commit_list_insert(j->item, &reversed);
- index_fd = hold_locked_index(lock, 1);
+ hold_locked_index(lock, 1);
clean = merge_recursive(&o, head,
remoteheads->item, reversed, &result);
if (active_cache_changed &&
- (write_cache(index_fd, active_cache, active_nr) ||
- commit_locked_index(lock)))
+ write_locked_index(&the_index, lock, COMMIT_LOCK))
die (_("unable to write %s"), get_index_file());
rollback_lock_file(lock);
return clean ? 0 : 1;
@@ -843,17 +839,15 @@ static void prepare_to_commit(struct commit_list *remoteheads)
static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
{
unsigned char result_tree[20], result_commit[20];
- struct commit_list *parent = xmalloc(sizeof(*parent));
+ struct commit_list *parents, **pptr = &parents;
write_tree_trivial(result_tree);
printf(_("Wonderful.\n"));
- parent->item = head;
- parent->next = xmalloc(sizeof(*parent->next));
- parent->next->item = remoteheads->item;
- parent->next->next = NULL;
+ pptr = commit_list_append(head, pptr);
+ pptr = commit_list_append(remoteheads->item, pptr);
prepare_to_commit(remoteheads);
- if (commit_tree(&merge_msg, result_tree, parent, 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"));
finish(head, remoteheads, result_commit, "In-index merge");
drop_save();
@@ -877,8 +871,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 +1276,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);
}
}
diff --git a/builtin/mv.c b/builtin/mv.c
index 180ef99127..6ffe540c20 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -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"));
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index c824d4ec5f..3c8f319be6 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -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);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index de36c60ca1..238b5021eb 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -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;
diff --git a/builtin/push.c b/builtin/push.c
index f8dfea41e1..f50e3d5e77 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -127,11 +127,10 @@ static NORETURN int die_push_simple(struct branch *branch, struct remote *remote
* them the big ugly fully qualified ref.
*/
const char *advice_maybe = "";
- const char *short_upstream =
- skip_prefix(branch->merge[0]->src, "refs/heads/");
+ const char *short_upstream = branch->merge[0]->src;
+
+ skip_prefix(short_upstream, "refs/heads/", &short_upstream);
- if (!short_upstream)
- short_upstream = branch->merge[0]->src;
/*
* Don't show advice for people who explicitly set
* push.default.
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 0d7ef847a7..e7e1c33a7f 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -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;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index c3230817db..92561bffc1 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -438,7 +438,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
uint32_t mask = 1 << (cmd->index % 32);
int i;
- trace_printf_key("GIT_TRACE_SHALLOW",
+ trace_printf_key(&trace_shallow,
"shallow: update_shallow_ref %s\n", cmd->ref_name);
for (i = 0; i < si->shallow->nr; i++)
if (si->used_shallow[i] &&
@@ -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;
diff --git a/builtin/remote.c b/builtin/remote.c
index c9102e8fe9..9a4640dbf0 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -250,9 +250,7 @@ static struct string_list branch_list;
static const char *abbrev_ref(const char *name, const char *prefix)
{
- const char *abbrev = skip_prefix(name, prefix);
- if (abbrev)
- return abbrev;
+ skip_prefix(name, prefix, &name);
return name;
}
#define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
@@ -265,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;
@@ -755,7 +754,7 @@ static int remove_branches(struct string_list *branches)
branch_names = xmalloc(branches->nr * sizeof(*branch_names));
for (i = 0; i < branches->nr; i++)
branch_names[i] = branches->items[i].string;
- result |= repack_without_refs(branch_names, branches->nr);
+ result |= repack_without_refs(branch_names, branches->nr, NULL);
free(branch_names);
for (i = 0; i < branches->nr; i++) {
@@ -1333,7 +1332,8 @@ static int prune_remote(const char *remote, int dry_run)
for (i = 0; i < states.stale.nr; i++)
delete_refs[i] = states.stale.items[i].util;
if (!dry_run)
- result |= repack_without_refs(delete_refs, states.stale.nr);
+ result |= repack_without_refs(delete_refs,
+ states.stale.nr, NULL);
free(delete_refs);
}
diff --git a/builtin/repack.c b/builtin/repack.c
index 6b0b62dcb2..a77e743b94 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -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);
diff --git a/builtin/replace.c b/builtin/replace.c
index 1bb491d3c4..294b61b97e 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -13,19 +13,21 @@
#include "refs.h"
#include "parse-options.h"
#include "run-command.h"
+#include "tag.h"
static const char * const git_replace_usage[] = {
N_("git replace [-f] <object> <replacement>"),
N_("git replace [-f] --edit <object>"),
+ N_("git replace [-f] --graft <commit> [<parent>...]"),
N_("git replace -d <object>..."),
N_("git replace [--format=<format>] [-l [<pattern>]]"),
NULL
};
enum replace_format {
- REPLACE_FORMAT_SHORT,
- REPLACE_FORMAT_MEDIUM,
- REPLACE_FORMAT_LONG
+ REPLACE_FORMAT_SHORT,
+ REPLACE_FORMAT_MEDIUM,
+ REPLACE_FORMAT_LONG
};
struct show_data {
@@ -188,27 +190,32 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f
}
/*
- * Write the contents of the object named by "sha1" to the file "filename",
- * pretty-printed for human editing based on its type.
+ * Write the contents of the object named by "sha1" to the file "filename".
+ * If "raw" is true, then the object's raw contents are printed according to
+ * "type". Otherwise, we pretty-print the contents for human editing.
*/
-static void export_object(const unsigned char *sha1, const char *filename)
+static void export_object(const unsigned char *sha1, enum object_type type,
+ int raw, const char *filename)
{
- const char *argv[] = { "--no-replace-objects", "cat-file", "-p", NULL, NULL };
- struct child_process cmd = { argv };
+ struct child_process cmd = { NULL };
int fd;
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0)
die_errno("unable to open %s for writing", filename);
- argv[3] = sha1_to_hex(sha1);
+ argv_array_push(&cmd.args, "--no-replace-objects");
+ argv_array_push(&cmd.args, "cat-file");
+ if (raw)
+ argv_array_push(&cmd.args, typename(type));
+ else
+ argv_array_push(&cmd.args, "-p");
+ argv_array_push(&cmd.args, sha1_to_hex(sha1));
cmd.git_cmd = 1;
cmd.out = fd;
if (run_command(&cmd))
die("cat-file reported failure");
-
- close(fd);
}
/*
@@ -217,7 +224,7 @@ static void export_object(const unsigned char *sha1, const char *filename)
* The sha1 of the written object is returned via sha1.
*/
static void import_object(unsigned char *sha1, enum object_type type,
- const char *filename)
+ int raw, const char *filename)
{
int fd;
@@ -225,7 +232,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
if (fd < 0)
die_errno("unable to open %s for reading", filename);
- if (type == OBJ_TREE) {
+ if (!raw && type == OBJ_TREE) {
const char *argv[] = { "mktree", NULL };
struct child_process cmd = { argv };
struct strbuf result = STRBUF_INIT;
@@ -265,7 +272,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
*/
}
-static int edit_and_replace(const char *object_ref, int force)
+static int edit_and_replace(const char *object_ref, int force, int raw)
{
char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
enum object_type type;
@@ -281,10 +288,10 @@ static int edit_and_replace(const char *object_ref, int force)
check_ref_valid(old, prev, ref, sizeof(ref), force);
- export_object(old, tmpfile);
+ export_object(old, type, raw, tmpfile);
if (launch_editor(tmpfile, NULL, NULL) < 0)
die("editing object file failed");
- import_object(new, type, tmpfile);
+ import_object(new, type, raw, tmpfile);
free(tmpfile);
@@ -294,22 +301,137 @@ static int edit_and_replace(const char *object_ref, int force)
return replace_object_sha1(object_ref, old, "replacement", new, force);
}
+static void replace_parents(struct strbuf *buf, int argc, const char **argv)
+{
+ struct strbuf new_parents = STRBUF_INIT;
+ const char *parent_start, *parent_end;
+ int i;
+
+ /* find existing parents */
+ parent_start = buf->buf;
+ parent_start += 46; /* "tree " + "hex sha1" + "\n" */
+ parent_end = parent_start;
+
+ while (starts_with(parent_end, "parent "))
+ parent_end += 48; /* "parent " + "hex sha1" + "\n" */
+
+ /* prepare new parents */
+ for (i = 0; i < argc; i++) {
+ unsigned char sha1[20];
+ if (get_sha1(argv[i], sha1) < 0)
+ die(_("Not a valid object name: '%s'"), argv[i]);
+ lookup_commit_or_die(sha1, argv[i]);
+ strbuf_addf(&new_parents, "parent %s\n", sha1_to_hex(sha1));
+ }
+
+ /* replace existing parents with new ones */
+ strbuf_splice(buf, parent_start - buf->buf, parent_end - parent_start,
+ new_parents.buf, new_parents.len);
+
+ strbuf_release(&new_parents);
+}
+
+struct check_mergetag_data {
+ int argc;
+ const char **argv;
+};
+
+static void check_one_mergetag(struct commit *commit,
+ struct commit_extra_header *extra,
+ void *data)
+{
+ struct check_mergetag_data *mergetag_data = (struct check_mergetag_data *)data;
+ const char *ref = mergetag_data->argv[0];
+ unsigned char tag_sha1[20];
+ struct tag *tag;
+ int i;
+
+ hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_sha1);
+ tag = lookup_tag(tag_sha1);
+ if (!tag)
+ die(_("bad mergetag in commit '%s'"), ref);
+ if (parse_tag_buffer(tag, extra->value, extra->len))
+ die(_("malformed mergetag in commit '%s'"), ref);
+
+ /* iterate over new parents */
+ for (i = 1; i < mergetag_data->argc; i++) {
+ unsigned char sha1[20];
+ if (get_sha1(mergetag_data->argv[i], sha1) < 0)
+ die(_("Not a valid object name: '%s'"), mergetag_data->argv[i]);
+ if (!hashcmp(tag->tagged->sha1, sha1))
+ return; /* found */
+ }
+
+ die(_("original commit '%s' contains mergetag '%s' that is discarded; "
+ "use --edit instead of --graft"), ref, sha1_to_hex(tag_sha1));
+}
+
+static void check_mergetags(struct commit *commit, int argc, const char **argv)
+{
+ struct check_mergetag_data mergetag_data;
+
+ mergetag_data.argc = argc;
+ mergetag_data.argv = argv;
+ for_each_mergetag(check_one_mergetag, commit, &mergetag_data);
+}
+
+static int create_graft(int argc, const char **argv, int force)
+{
+ unsigned char old[20], new[20];
+ const char *old_ref = argv[0];
+ struct commit *commit;
+ struct strbuf buf = STRBUF_INIT;
+ const char *buffer;
+ unsigned long size;
+
+ if (get_sha1(old_ref, old) < 0)
+ die(_("Not a valid object name: '%s'"), old_ref);
+ commit = lookup_commit_or_die(old, old_ref);
+
+ buffer = get_commit_buffer(commit, &size);
+ strbuf_add(&buf, buffer, size);
+ unuse_commit_buffer(commit, buffer);
+
+ replace_parents(&buf, argc - 1, &argv[1]);
+
+ if (remove_signature(&buf)) {
+ warning(_("the original commit '%s' has a gpg signature."), old_ref);
+ warning(_("the signature will be removed in the replacement commit!"));
+ }
+
+ check_mergetags(commit, argc, argv);
+
+ if (write_sha1_file(buf.buf, buf.len, commit_type, new))
+ die(_("could not write replacement commit for: '%s'"), old_ref);
+
+ strbuf_release(&buf);
+
+ if (!hashcmp(old, new))
+ return error("new commit is the same as the old one: '%s'", sha1_to_hex(old));
+
+ return replace_object_sha1(old_ref, old, "replacement", new, force);
+}
+
int cmd_replace(int argc, const char **argv, const char *prefix)
{
int force = 0;
+ int raw = 0;
const char *format = NULL;
enum {
MODE_UNSPECIFIED = 0,
MODE_LIST,
MODE_DELETE,
MODE_EDIT,
+ MODE_GRAFT,
MODE_REPLACE
} cmdmode = MODE_UNSPECIFIED;
struct option options[] = {
OPT_CMDMODE('l', "list", &cmdmode, N_("list replace refs"), MODE_LIST),
OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
+ OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT),
OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
+ OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")),
OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
OPT_END()
};
@@ -325,10 +447,17 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
usage_msg_opt("--format cannot be used when not listing",
git_replace_usage, options);
- if (force && cmdmode != MODE_REPLACE && cmdmode != MODE_EDIT)
+ if (force &&
+ cmdmode != MODE_REPLACE &&
+ cmdmode != MODE_EDIT &&
+ cmdmode != MODE_GRAFT)
usage_msg_opt("-f only makes sense when writing a replacement",
git_replace_usage, options);
+ if (raw && cmdmode != MODE_EDIT)
+ usage_msg_opt("--raw only makes sense with --edit",
+ git_replace_usage, options);
+
switch (cmdmode) {
case MODE_DELETE:
if (argc < 1)
@@ -346,7 +475,13 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
if (argc != 1)
usage_msg_opt("-e needs exactly one argument",
git_replace_usage, options);
- return edit_and_replace(argv[0], force);
+ return edit_and_replace(argv[0], force, raw);
+
+ case MODE_GRAFT:
+ if (argc < 1)
+ usage_msg_opt("-g needs at least one argument",
+ git_replace_usage, options);
+ return create_graft(argc, argv, force);
case MODE_LIST:
if (argc > 1)
diff --git a/builtin/reset.c b/builtin/reset.c
index f368266762..855d478e3b 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -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."));
}
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 9f92905379..ff84a825ff 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -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,
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 1a6122d3ae..8102aaa924 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -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;
diff --git a/builtin/rm.c b/builtin/rm.c
index 960634dd0c..bc6490b8bc 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -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"));
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index d87317290c..5fd4e4e488 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -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);
diff --git a/builtin/tag.c b/builtin/tag.c
index c6e8a71127..19eb747820 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -32,6 +32,8 @@ static const char * const git_tag_usage[] = {
#define SORT_MASK 0x7fff
#define REVERSE_SORT 0x8000
+static int tag_sort;
+
struct tag_filter {
const char **patterns;
int lines;
@@ -83,7 +85,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
};
/*
@@ -346,9 +348,51 @@ static const char tag_template_nocleanup[] =
"Lines starting with '%c' will be kept; you may remove them"
" yourself if you want to.\n");
+/*
+ * Parse a sort string, and return 0 if parsed successfully. Will return
+ * non-zero when the sort string does not parse into a known type. If var is
+ * given, the error message becomes a warning and includes information about
+ * the configuration value.
+ */
+static int parse_sort_string(const char *var, const char *arg, int *sort)
+{
+ int type = 0, flags = 0;
+
+ if (skip_prefix(arg, "-", &arg))
+ flags |= REVERSE_SORT;
+
+ if (skip_prefix(arg, "version:", &arg) || skip_prefix(arg, "v:", &arg))
+ type = VERCMP_SORT;
+ else
+ type = STRCMP_SORT;
+
+ if (strcmp(arg, "refname")) {
+ if (!var)
+ return error(_("unsupported sort specification '%s'"), arg);
+ else {
+ warning(_("unsupported sort specification '%s' in variable '%s'"),
+ var, arg);
+ return -1;
+ }
+ }
+
+ *sort = (type | flags);
+
+ return 0;
+}
+
static int git_tag_config(const char *var, const char *value, void *cb)
{
- int status = git_gpg_config(var, value, cb);
+ int status;
+
+ if (!strcmp(var, "tag.sort")) {
+ if (!value)
+ return config_error_nonbool(var);
+ parse_sort_string(var, value, &tag_sort);
+ return 0;
+ }
+
+ status = git_gpg_config(var, value, cb);
if (status)
return status;
if (starts_with(var, "column."))
@@ -522,24 +566,8 @@ static int parse_opt_points_at(const struct option *opt __attribute__((unused)),
static int parse_opt_sort(const struct option *opt, const char *arg, int unset)
{
int *sort = opt->value;
- int flags = 0;
- if (*arg == '-') {
- flags |= REVERSE_SORT;
- arg++;
- }
- if (starts_with(arg, "version:")) {
- *sort = VERCMP_SORT;
- arg += 8;
- } else if (starts_with(arg, "v:")) {
- *sort = VERCMP_SORT;
- arg += 2;
- } else
- *sort = STRCMP_SORT;
- if (strcmp(arg, "refname"))
- die(_("unsupported sort specification %s"), arg);
- *sort |= flags;
- return 0;
+ return parse_sort_string(NULL, arg, sort);
}
int cmd_tag(int argc, const char **argv, const char *prefix)
@@ -552,7 +580,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct create_tag_options opt;
char *cleanup_arg = NULL;
int annotate = 0, force = 0, lines = -1;
- int cmdmode = 0, sort = 0;
+ int cmdmode = 0;
const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT };
struct commit_list *with_commit = NULL;
@@ -578,7 +606,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT__FORCE(&force, N_("replace the tag if exists")),
OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
{
- OPTION_CALLBACK, 0, "sort", &sort, N_("type"), N_("sort tags"),
+ OPTION_CALLBACK, 0, "sort", &tag_sort, N_("type"), N_("sort tags"),
PARSE_OPT_NONEG, parse_opt_sort
},
@@ -634,9 +662,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
copts.padding = 2;
run_column_filter(colopts, &copts);
}
- if (lines != -1 && sort)
+ if (lines != -1 && tag_sort)
die(_("--sort and -n are incompatible"));
- ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, sort);
+ ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, tag_sort);
if (column_active(colopts))
stop_column_filter();
return ret;
diff --git a/builtin/update-index.c b/builtin/update-index.c
index ebea285e1b..e8c7fd4d49 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -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/update-ref.c b/builtin/update-ref.c
index 405267f6e2..3067b11310 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -16,6 +16,7 @@ static struct ref_transaction *transaction;
static char line_termination = '\n';
static int update_flags;
+static struct strbuf err = STRBUF_INIT;
/*
* Parse one whitespace- or NUL-terminated, possibly C-quoted argument
@@ -197,8 +198,9 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next)
if (*next != line_termination)
die("update %s: extra input: %s", refname, next);
- ref_transaction_update(transaction, refname, new_sha1, old_sha1,
- update_flags, have_old);
+ if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
+ update_flags, have_old, &err))
+ die("%s", err.buf);
update_flags = 0;
free(refname);
@@ -286,8 +288,9 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
if (*next != line_termination)
die("verify %s: extra input: %s", refname, next);
- ref_transaction_update(transaction, refname, new_sha1, old_sha1,
- update_flags, have_old);
+ if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
+ update_flags, have_old, &err))
+ die("%s", err.buf);
update_flags = 0;
free(refname);
@@ -359,17 +362,16 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
die("Refusing to perform update with empty message.");
if (read_stdin) {
- int ret;
transaction = ref_transaction_begin();
-
if (delete || no_deref || argc > 0)
usage_with_options(git_update_ref_usage, options);
if (end_null)
line_termination = '\0';
update_refs_stdin();
- ret = ref_transaction_commit(transaction, msg,
- UPDATE_REFS_DIE_ON_ERR);
- return ret;
+ if (ref_transaction_commit(transaction, msg, &err))
+ die("%s", err.buf);
+ ref_transaction_free(transaction);
+ return 0;
}
if (end_null)
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
new file mode 100644
index 0000000000..b0f85042b2
--- /dev/null
+++ b/builtin/verify-commit.c
@@ -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;
+}
diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c
index 66cd2df0f8..972579f33c 100644
--- a/builtin/verify-pack.c
+++ b/builtin/verify-pack.c
@@ -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));
diff --git a/bundle.c b/bundle.c
index 1222952075..71a21a67fa 100644
--- a/bundle.c
+++ b/bundle.c
@@ -237,8 +237,6 @@ int create_bundle(struct bundle_header *header, const char *path,
static struct lock_file lock;
int bundle_fd = -1;
int bundle_to_stdout;
- struct argv_array argv_boundary = ARGV_ARRAY_INIT;
- struct argv_array argv_pack = ARGV_ARRAY_INIT;
int i, ref_count = 0;
struct strbuf buf = STRBUF_INIT;
struct rev_info revs;
@@ -260,14 +258,12 @@ int create_bundle(struct bundle_header *header, const char *path,
init_revisions(&revs, NULL);
/* write prerequisites */
- argv_array_pushl(&argv_boundary,
+ memset(&rls, 0, sizeof(rls));
+ argv_array_pushl(&rls.args,
"rev-list", "--boundary", "--pretty=oneline",
NULL);
for (i = 1; i < argc; i++)
- argv_array_push(&argv_boundary, argv[i]);
-
- memset(&rls, 0, sizeof(rls));
- rls.argv = argv_boundary.argv;
+ argv_array_push(&rls.args, argv[i]);
rls.out = -1;
rls.git_cmd = 1;
if (start_command(&rls))
@@ -382,12 +378,11 @@ int create_bundle(struct bundle_header *header, const char *path,
write_or_die(bundle_fd, "\n", 1);
/* write pack */
- argv_array_pushl(&argv_pack,
+ memset(&rls, 0, sizeof(rls));
+ argv_array_pushl(&rls.args,
"pack-objects", "--all-progress-implied",
"--stdout", "--thin", "--delta-base-offset",
NULL);
- memset(&rls, 0, sizeof(rls));
- rls.argv = argv_pack.argv;
rls.in = -1;
rls.out = bundle_fd;
rls.git_cmd = 1;
diff --git a/cache-tree.c b/cache-tree.c
index 7fa524a113..c53f7de2b1 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -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);
}
diff --git a/cache-tree.h b/cache-tree.h
index f1923ad1e9..b47ccec7f6 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -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 cbe1935ba6..fcb511db70 100644
--- a/cache.h
+++ b/cache.h
@@ -7,6 +7,7 @@
#include "advice.h"
#include "gettext.h"
#include "convert.h"
+#include "trace.h"
#include SHA1_HEADER
#ifndef git_SHA_CTX
@@ -150,6 +151,7 @@ struct cache_entry {
unsigned int ce_mode;
unsigned int ce_flags;
unsigned int ce_namelen;
+ unsigned int index; /* for link extension */
unsigned char sha1[20];
char name[FLEX_ARRAY]; /* more */
};
@@ -160,7 +162,7 @@ struct cache_entry {
#define CE_STAGESHIFT 12
/*
- * Range 0xFFFF0000 in ce_flags is divided into
+ * Range 0xFFFF0FFF in ce_flags is divided into
* two parts: in-memory flags and on-disk ones.
* Flags in CE_EXTENDED_FLAGS will get saved on-disk
* if you want to save a new flag, add it in
@@ -183,6 +185,9 @@ struct cache_entry {
/* used to temporarily mark paths matched by pathspecs */
#define CE_MATCHED (1 << 26)
+#define CE_UPDATE_IN_BASE (1 << 27)
+#define CE_STRIP_NAME (1 << 28)
+
/*
* Extended on-disk flags
*/
@@ -283,12 +288,22 @@ static inline unsigned int canon_mode(unsigned int mode)
#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
+#define SOMETHING_CHANGED (1 << 0) /* unclassified changes go here */
+#define CE_ENTRY_CHANGED (1 << 1)
+#define CE_ENTRY_REMOVED (1 << 2)
+#define CE_ENTRY_ADDED (1 << 3)
+#define RESOLVE_UNDO_CHANGED (1 << 4)
+#define CACHE_TREE_CHANGED (1 << 5)
+#define SPLIT_INDEX_ORDERED (1 << 6)
+
+struct split_index;
struct index_state {
struct cache_entry **cache;
unsigned int version;
unsigned int cache_nr, cache_alloc, cache_changed;
struct string_list *resolve_undo;
struct cache_tree *cache_tree;
+ struct split_index *split_index;
struct cache_time timestamp;
unsigned name_hash_initialized : 1,
initialized : 1;
@@ -317,7 +332,6 @@ extern void free_name_hash(struct index_state *istate);
#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
#define is_cache_unborn() is_index_unborn(&the_index)
#define read_cache_unmerged() read_index_unmerged(&the_index)
-#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
#define discard_cache() discard_index(&the_index)
#define unmerged_cache() unmerged_index(&the_index)
#define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
@@ -472,12 +486,17 @@ extern int daemonize(void);
} while (0)
/* Initialize and use the cache information */
+struct lock_file;
extern int read_index(struct index_state *);
extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
+extern int do_read_index(struct index_state *istate, const char *path,
+ int must_exist); /* for testting only! */
extern int read_index_from(struct index_state *, const char *path);
extern int is_index_unborn(struct index_state *);
extern int read_index_unmerged(struct index_state *);
-extern int write_index(struct index_state *, int newfd);
+#define COMMIT_LOCK (1 << 0)
+#define CLOSE_LOCK (1 << 1)
+extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
extern int discard_index(struct index_state *);
extern int unmerged_index(const struct index_state *);
extern int verify_path(const char *path);
@@ -489,6 +508,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
#define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */
#define ADD_CACHE_NEW_ONLY 16 /* Do not replace existing ones */
+#define ADD_CACHE_KEEP_CACHE_TREE 32 /* Do not invalidate cache-tree */
extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
extern int remove_index_entry_at(struct index_state *, int pos);
@@ -559,6 +579,8 @@ struct lock_file {
#define LOCK_DIE_ON_ERROR 1
#define LOCK_NODEREF 2
extern int unable_to_lock_error(const char *path, int err);
+extern void unable_to_lock_message(const char *path, int err,
+ struct strbuf *buf);
extern NORETURN void unable_to_lock_index_die(const char *path, int err);
extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
@@ -566,7 +588,6 @@ extern int commit_lock_file(struct lock_file *);
extern void update_index_if_able(struct index_state *, struct lock_file *);
extern int hold_locked_index(struct lock_file *, int);
-extern int commit_locked_index(struct lock_file *);
extern void set_alternate_index_output(const char *);
extern int close_lock_file(struct lock_file *);
extern void rollback_lock_file(struct lock_file *);
@@ -977,7 +998,7 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
* give up and return NULL.
*
- * errno is sometimes set on errors, but not always.
+ * errno is set to something meaningful on error.
*/
extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
@@ -999,7 +1020,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 +1099,7 @@ const char *show_ident_date(const struct ident_split *id, enum date_mode mode);
extern int ident_cmp(const struct ident_split *, const struct ident_split *);
struct checkout {
+ struct index_state *istate;
const char *base_dir;
int base_dir_len;
unsigned force:1,
@@ -1090,12 +1112,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);
@@ -1376,18 +1402,9 @@ extern void *alloc_commit_node(void);
extern void *alloc_tag_node(void);
extern void *alloc_object_node(void);
extern void alloc_report(void);
+extern unsigned int alloc_commit_index(void);
-/* trace.c */
-__attribute__((format (printf, 1, 2)))
-extern void trace_printf(const char *format, ...);
-__attribute__((format (printf, 2, 3)))
-extern void trace_argv_printf(const char **argv, const char *format, ...);
-extern void trace_repo_setup(const char *prefix);
-extern int trace_want(const char *key);
-__attribute__((format (printf, 2, 3)))
-extern void trace_printf_key(const char *key, const char *fmt, ...);
-extern void trace_strbuf(const char *key, const struct strbuf *buf);
-
+/* pkt-line.c */
void packet_trace_identity(const char *prog);
/* add */
diff --git a/check_bindir b/check_bindir
index a1c4c3e8d8..623eadcbb7 100755
--- a/check_bindir
+++ b/check_bindir
@@ -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."
diff --git a/column.c b/column.c
index 1a468debb4..ca878bcea7 100644
--- a/column.c
+++ b/column.c
@@ -336,8 +336,9 @@ static int column_config(const char *var, const char *value,
int git_column_config(const char *var, const char *value,
const char *command, unsigned int *colopts)
{
- const char *it = skip_prefix(var, "column.");
- if (!it)
+ const char *it;
+
+ if (!skip_prefix(var, "column.", &it))
return 0;
if (!strcmp(it, "ui"))
diff --git a/command-list.txt b/command-list.txt
index cf36c3d71e..a3ff0c9e60 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -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
diff --git a/commit-slab.h b/commit-slab.h
index cc114b53b0..375c9c751a 100644
--- a/commit-slab.h
+++ b/commit-slab.h
@@ -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 */
diff --git a/commit.c b/commit.c
index 881be3baa3..ae7f2b10f4 100644
--- a/commit.c
+++ b/commit.c
@@ -17,20 +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,
- int quiet)
-{
- if (obj->type != OBJ_COMMIT) {
- if (!quiet)
- error("Object %s is a %s, not a commit",
- sha1_to_hex(sha1), typename(obj->type));
- return NULL;
- }
- return (struct commit *) obj;
-}
struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
int quiet)
@@ -39,7 +25,7 @@ struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
if (!obj)
return NULL;
- return check_commit(obj, sha1, quiet);
+ return object_as_type(obj, OBJ_COMMIT, quiet);
}
struct commit *lookup_commit_reference(const unsigned char *sha1)
@@ -62,14 +48,9 @@ struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_n
struct commit *lookup_commit(const unsigned char *sha1)
{
struct object *obj = lookup_object(sha1);
- if (!obj) {
- struct commit *c = alloc_commit_node();
- c->index = commit_count++;
- return create_object(sha1, OBJ_COMMIT, c);
- }
- if (!obj->type)
- obj->type = OBJ_COMMIT;
- return check_commit(obj, sha1, 0);
+ if (!obj)
+ return create_object(sha1, alloc_commit_node());
+ return object_as_type(obj, OBJ_COMMIT, 0);
}
struct commit *lookup_commit_reference_by_name(const char *name)
@@ -247,6 +228,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 +375,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);
@@ -379,12 +430,7 @@ struct commit_list *copy_commit_list(struct commit_list *list)
struct commit_list *head = NULL;
struct commit_list **pp = &head;
while (list) {
- struct commit_list *new;
- new = xmalloc(sizeof(struct commit_list));
- new->item = list->item;
- new->next = NULL;
- *pp = new;
- pp = &new->next;
+ pp = commit_list_append(list->item, pp);
list = list->next;
}
return head;
@@ -539,25 +585,14 @@ 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');
- ident_line = skip_prefix(buf, "author ");
- if (!ident_line) {
+ if (!skip_prefix(buf, "author ", &ident_line)) {
if (!line_end[0] || line_end[1] == '\n')
return; /* end of header */
continue;
@@ -575,7 +610,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_,
@@ -729,45 +764,41 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so
static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
-static struct commit *interesting(struct commit_list *list)
+static int queue_has_nonstale(struct prio_queue *queue)
{
- while (list) {
- struct commit *commit = list->item;
- list = list->next;
- if (commit->object.flags & STALE)
- continue;
- return commit;
+ int i;
+ for (i = 0; i < queue->nr; i++) {
+ struct commit *commit = queue->array[i].data;
+ if (!(commit->object.flags & STALE))
+ return 1;
}
- return NULL;
+ return 0;
}
/* all input commits in one and twos[] must have been parsed! */
static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos)
{
- struct commit_list *list = NULL;
+ struct prio_queue queue = { compare_commits_by_commit_date };
struct commit_list *result = NULL;
int i;
one->object.flags |= PARENT1;
- commit_list_insert_by_date(one, &list);
- if (!n)
- return list;
+ if (!n) {
+ commit_list_append(one, &result);
+ return result;
+ }
+ prio_queue_put(&queue, one);
+
for (i = 0; i < n; i++) {
twos[i]->object.flags |= PARENT2;
- commit_list_insert_by_date(twos[i], &list);
+ prio_queue_put(&queue, twos[i]);
}
- while (interesting(list)) {
- struct commit *commit;
+ while (queue_has_nonstale(&queue)) {
+ struct commit *commit = prio_queue_get(&queue);
struct commit_list *parents;
- struct commit_list *next;
int flags;
- commit = list->item;
- next = list->next;
- free(list);
- list = next;
-
flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
if (flags == (PARENT1 | PARENT2)) {
if (!(commit->object.flags & RESULT)) {
@@ -786,11 +817,11 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n, struc
if (parse_commit(p))
return NULL;
p->object.flags |= flags;
- commit_list_insert_by_date(p, &list);
+ prio_queue_put(&queue, p);
}
}
- free_commit_list(list);
+ clear_prio_queue(&queue);
return result;
}
@@ -935,12 +966,7 @@ struct commit_list *get_merge_bases_many(struct commit *one,
}
/* There are more than one */
- cnt = 0;
- list = result;
- while (list) {
- list = list->next;
- cnt++;
- }
+ cnt = commit_list_count(result);
rslt = xcalloc(cnt, sizeof(*rslt));
for (list = result, i = 0; list; list = list->next)
rslt[i++] = list->item;
@@ -1080,17 +1106,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;
@@ -1098,7 +1121,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] == ' ')
@@ -1119,11 +1142,44 @@ int parse_signed_commit(const unsigned char *sha1,
}
line = next;
}
- cleanup:
- free(buffer);
+ unuse_commit_buffer(commit, buffer);
return saw_signature;
}
+int remove_signature(struct strbuf *buf)
+{
+ const char *line = buf->buf;
+ const char *tail = buf->buf + buf->len;
+ int in_signature = 0;
+ const char *sig_start = NULL;
+ const char *sig_end = NULL;
+
+ while (line < tail) {
+ const char *next = memchr(line, '\n', tail - line);
+ next = next ? next + 1 : tail;
+
+ if (in_signature && line[0] == ' ')
+ sig_end = next;
+ else if (starts_with(line, gpg_sig_header) &&
+ line[gpg_sig_header_len] == ' ') {
+ sig_start = line;
+ sig_end = next;
+ in_signature = 1;
+ } else {
+ if (*line == '\n')
+ /* dump the whole remainder of the buffer */
+ next = tail;
+ in_signature = 0;
+ }
+ line = next;
+ }
+
+ if (sig_start)
+ strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start);
+
+ return sig_start != NULL;
+}
+
static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
{
struct merge_remote_desc *desc;
@@ -1183,8 +1239,7 @@ static void parse_gpg_output(struct signature_check *sigc)
for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
const char *found, *next;
- found = skip_prefix(buf, sigcheck_gpg_status[i].check + 1);
- if (!found) {
+ if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
found = strstr(buf, sigcheck_gpg_status[i].check);
if (!found)
continue;
@@ -1211,14 +1266,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);
@@ -1257,14 +1312,25 @@ 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;
}
+void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data)
+{
+ struct commit_extra_header *extra, *to_free;
+
+ to_free = read_commit_extra_headers(commit, NULL);
+ for (extra = to_free; extra; extra = extra->next) {
+ if (strcmp(extra->key, "mergetag"))
+ continue; /* not a merge tag */
+ fn(commit, extra, data);
+ }
+ free_commit_extra_headers(to_free);
+}
+
static inline int standard_header_field(const char *field, size_t len)
{
return ((len == 4 && !memcmp(field, "tree ", 5)) ||
@@ -1344,7 +1410,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)
{
@@ -1352,7 +1419,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;
@@ -1473,7 +1540,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)
@@ -1484,7 +1552,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 */
@@ -1523,7 +1591,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))
diff --git a/commit.h b/commit.h
index a9f177ba48..a8cbf52f15 100644
--- 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);
@@ -235,6 +271,7 @@ extern void assign_shallow_commits_to_refs(struct shallow_info *info,
int *ref_status);
extern int delayed_reachability_test(struct shallow_info *si, int c);
extern void prune_shallow(int show_only);
+extern struct trace_key trace_shallow;
int is_descendant_of(struct commit *, struct commit_list *);
int in_merge_bases(struct commit *, struct commit *);
@@ -261,11 +298,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 *);
@@ -274,6 +313,11 @@ extern struct commit_extra_header *read_commit_extra_headers(struct commit *, co
extern void free_commit_extra_headers(struct commit_extra_header *extra);
+typedef void (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
+ void *cb_data);
+
+extern void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data);
+
struct merge_remote_desc {
struct object *obj; /* the named object, could be a tag */
const char *name;
@@ -287,8 +331,10 @@ 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 int remove_signature(struct strbuf *buf);
+
extern void print_commit_list(struct commit_list *list,
const char *format_cur,
const char *format_last);
diff --git a/compat/mingw.c b/compat/mingw.c
index a0e13bc862..9d435e2cf4 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1,6 +1,7 @@
#include "../git-compat-util.h"
#include "win32.h"
#include <conio.h>
+#include <wchar.h>
#include "../strbuf.h"
#include "../run-command.h"
@@ -198,14 +199,16 @@ static int ask_yes_no_if_possible(const char *format, ...)
}
}
-#undef unlink
int mingw_unlink(const char *pathname)
{
int ret, tries = 0;
+ wchar_t wpathname[MAX_PATH];
+ if (xutftowcs_path(wpathname, pathname) < 0)
+ return -1;
/* read-only files cannot be removed */
- chmod(pathname, 0666);
- while ((ret = unlink(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
+ _wchmod(wpathname, 0666);
+ while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
if (!is_file_in_use_error(GetLastError()))
break;
/*
@@ -221,45 +224,45 @@ int mingw_unlink(const char *pathname)
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
ask_yes_no_if_possible("Unlink of file '%s' failed. "
"Should I try again?", pathname))
- ret = unlink(pathname);
+ ret = _wunlink(wpathname);
return ret;
}
-static int is_dir_empty(const char *path)
+static int is_dir_empty(const wchar_t *wpath)
{
- struct strbuf buf = STRBUF_INIT;
- WIN32_FIND_DATAA findbuf;
+ WIN32_FIND_DATAW findbuf;
HANDLE handle;
-
- strbuf_addf(&buf, "%s\\*", path);
- handle = FindFirstFileA(buf.buf, &findbuf);
- if (handle == INVALID_HANDLE_VALUE) {
- strbuf_release(&buf);
+ wchar_t wbuf[MAX_PATH + 2];
+ wcscpy(wbuf, wpath);
+ wcscat(wbuf, L"\\*");
+ handle = FindFirstFileW(wbuf, &findbuf);
+ if (handle == INVALID_HANDLE_VALUE)
return GetLastError() == ERROR_NO_MORE_FILES;
- }
- while (!strcmp(findbuf.cFileName, ".") ||
- !strcmp(findbuf.cFileName, ".."))
- if (!FindNextFile(handle, &findbuf)) {
- strbuf_release(&buf);
- return GetLastError() == ERROR_NO_MORE_FILES;
+ while (!wcscmp(findbuf.cFileName, L".") ||
+ !wcscmp(findbuf.cFileName, L".."))
+ if (!FindNextFileW(handle, &findbuf)) {
+ DWORD err = GetLastError();
+ FindClose(handle);
+ return err == ERROR_NO_MORE_FILES;
}
FindClose(handle);
- strbuf_release(&buf);
return 0;
}
-#undef rmdir
int mingw_rmdir(const char *pathname)
{
int ret, tries = 0;
+ wchar_t wpathname[MAX_PATH];
+ if (xutftowcs_path(wpathname, pathname) < 0)
+ return -1;
- while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
+ while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
if (!is_file_in_use_error(GetLastError()))
errno = err_win_to_posix(GetLastError());
if (errno != EACCES)
break;
- if (!is_dir_empty(pathname)) {
+ if (!is_dir_empty(wpathname)) {
errno = ENOTEMPTY;
break;
}
@@ -276,16 +279,26 @@ int mingw_rmdir(const char *pathname)
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
"Should I try again?", pathname))
- ret = rmdir(pathname);
+ ret = _wrmdir(wpathname);
+ return ret;
+}
+
+int mingw_mkdir(const char *path, int mode)
+{
+ int ret;
+ wchar_t wpath[MAX_PATH];
+ if (xutftowcs_path(wpath, path) < 0)
+ return -1;
+ ret = _wmkdir(wpath);
return ret;
}
-#undef open
int mingw_open (const char *filename, int oflags, ...)
{
va_list args;
unsigned mode;
int fd;
+ wchar_t wfilename[MAX_PATH];
va_start(args, oflags);
mode = va_arg(args, int);
@@ -294,10 +307,12 @@ int mingw_open (const char *filename, int oflags, ...)
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
- fd = open(filename, oflags, mode);
+ if (xutftowcs_path(wfilename, filename) < 0)
+ return -1;
+ fd = _wopen(wfilename, oflags, mode);
if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
- DWORD attrs = GetFileAttributes(filename);
+ DWORD attrs = GetFileAttributesW(wfilename);
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
errno = EISDIR;
}
@@ -332,17 +347,28 @@ int mingw_fgetc(FILE *stream)
#undef fopen
FILE *mingw_fopen (const char *filename, const char *otype)
{
+ FILE *file;
+ wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
- return fopen(filename, otype);
+ if (xutftowcs_path(wfilename, filename) < 0 ||
+ xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
+ return NULL;
+ file = _wfopen(wfilename, wotype);
+ return file;
}
-#undef freopen
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
{
+ FILE *file;
+ wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
- return freopen(filename, otype, stream);
+ if (xutftowcs_path(wfilename, filename) < 0 ||
+ xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
+ return NULL;
+ file = _wfreopen(wfilename, wotype, stream);
+ return file;
}
#undef fflush
@@ -367,6 +393,31 @@ int mingw_fflush(FILE *stream)
return ret;
}
+int mingw_access(const char *filename, int mode)
+{
+ wchar_t wfilename[MAX_PATH];
+ if (xutftowcs_path(wfilename, filename) < 0)
+ return -1;
+ /* X_OK is not supported by the MSVCRT version */
+ return _waccess(wfilename, mode & ~X_OK);
+}
+
+int mingw_chdir(const char *dirname)
+{
+ wchar_t wdirname[MAX_PATH];
+ if (xutftowcs_path(wdirname, dirname) < 0)
+ return -1;
+ return _wchdir(wdirname);
+}
+
+int mingw_chmod(const char *filename, int mode)
+{
+ wchar_t wfilename[MAX_PATH];
+ if (xutftowcs_path(wfilename, filename) < 0)
+ return -1;
+ return _wchmod(wfilename, mode);
+}
+
/*
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
@@ -392,10 +443,12 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
*/
static int do_lstat(int follow, const char *file_name, struct stat *buf)
{
- int err;
WIN32_FILE_ATTRIBUTE_DATA fdata;
+ wchar_t wfilename[MAX_PATH];
+ if (xutftowcs_path(wfilename, file_name) < 0)
+ return -1;
- if (!(err = get_file_attr(file_name, &fdata))) {
+ if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
buf->st_ino = 0;
buf->st_gid = 0;
buf->st_uid = 0;
@@ -408,8 +461,8 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- WIN32_FIND_DATAA findbuf;
- HANDLE handle = FindFirstFileA(file_name, &findbuf);
+ WIN32_FIND_DATAW findbuf;
+ HANDLE handle = FindFirstFileW(wfilename, &findbuf);
if (handle != INVALID_HANDLE_VALUE) {
if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
@@ -428,7 +481,23 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
}
return 0;
}
- errno = err;
+ switch (GetLastError()) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_SHARING_VIOLATION:
+ case ERROR_LOCK_VIOLATION:
+ case ERROR_SHARING_BUFFER_EXCEEDED:
+ errno = EACCES;
+ break;
+ case ERROR_BUFFER_OVERFLOW:
+ errno = ENAMETOOLONG;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ errno = ENOMEM;
+ break;
+ default:
+ errno = ENOENT;
+ break;
+ }
return -1;
}
@@ -441,7 +510,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;
@@ -516,16 +585,20 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
{
FILETIME mft, aft;
int fh, rc;
+ DWORD attrs;
+ wchar_t wfilename[MAX_PATH];
+ if (xutftowcs_path(wfilename, file_name) < 0)
+ return -1;
/* must have write permission */
- DWORD attrs = GetFileAttributes(file_name);
+ attrs = GetFileAttributesW(wfilename);
if (attrs != INVALID_FILE_ATTRIBUTES &&
(attrs & FILE_ATTRIBUTE_READONLY)) {
/* ignore errors here; open() will report them */
- SetFileAttributes(file_name, attrs & ~FILE_ATTRIBUTE_READONLY);
+ SetFileAttributesW(wfilename, attrs & ~FILE_ATTRIBUTE_READONLY);
}
- if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0) {
+ if ((fh = _wopen(wfilename, O_RDWR | O_BINARY)) < 0) {
rc = -1;
goto revert_attrs;
}
@@ -548,7 +621,7 @@ revert_attrs:
if (attrs != INVALID_FILE_ATTRIBUTES &&
(attrs & FILE_ATTRIBUTE_READONLY)) {
/* ignore errors again */
- SetFileAttributes(file_name, attrs);
+ SetFileAttributesW(wfilename, attrs);
}
return rc;
}
@@ -559,6 +632,18 @@ unsigned int sleep (unsigned int seconds)
return 0;
}
+char *mingw_mktemp(char *template)
+{
+ wchar_t wtemplate[MAX_PATH];
+ if (xutftowcs_path(wtemplate, template) < 0)
+ return NULL;
+ if (!_wmktemp(wtemplate))
+ return NULL;
+ if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0)
+ return NULL;
+ return template;
+}
+
int mkstemp(char *template)
{
char *filename = mktemp(template);
@@ -617,17 +702,18 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
return result;
}
-#undef getcwd
char *mingw_getcwd(char *pointer, int len)
{
int i;
- char *ret = getcwd(pointer, len);
- if (!ret)
- return ret;
+ wchar_t wpointer[MAX_PATH];
+ if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
+ return NULL;
+ if (xwcstoutf(pointer, wpointer, len) < 0)
+ return NULL;
for (i = 0; pointer[i]; i++)
if (pointer[i] == '\\')
pointer[i] = '/';
- return ret;
+ return pointer;
}
/*
@@ -831,9 +917,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 +952,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 +978,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 +1003,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 +1037,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 +1081,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 +1099,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 +1114,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 +1136,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 +1145,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) {
@@ -1226,8 +1316,7 @@ static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
else
ai->ai_canonname = NULL;
- sin = xmalloc(ai->ai_addrlen);
- memset(sin, 0, ai->ai_addrlen);
+ sin = xcalloc(1, ai->ai_addrlen);
sin->sin_family = AF_INET;
/* Note: getaddrinfo is supposed to allow service to be a string,
* which should be looked up using getservbyname. This is
@@ -1481,33 +1570,36 @@ int mingw_rename(const char *pold, const char *pnew)
{
DWORD attrs, gle;
int tries = 0;
+ wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
+ if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
+ return -1;
/*
* Try native rename() first to get errno right.
* It is based on MoveFile(), which cannot overwrite existing files.
*/
- if (!rename(pold, pnew))
+ if (!_wrename(wpold, wpnew))
return 0;
if (errno != EEXIST)
return -1;
repeat:
- if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
+ if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
return 0;
/* TODO: translate more errors */
gle = GetLastError();
if (gle == ERROR_ACCESS_DENIED &&
- (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
+ (attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
errno = EISDIR;
return -1;
}
if ((attrs & FILE_ATTRIBUTE_READONLY) &&
- SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
- if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
+ SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
+ if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
return 0;
gle = GetLastError();
/* revert file attributes on failure */
- SetFileAttributes(pnew, attrs);
+ SetFileAttributesW(wpnew, attrs);
}
}
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
@@ -1753,11 +1845,16 @@ void mingw_open_html(const char *unixpath)
int link(const char *oldpath, const char *newpath)
{
- typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
+ typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
static T create_hard_link = NULL;
+ wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
+ if (xutftowcs_path(woldpath, oldpath) < 0 ||
+ xutftowcs_path(wnewpath, newpath) < 0)
+ return -1;
+
if (!create_hard_link) {
create_hard_link = (T) GetProcAddress(
- GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
+ GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
if (!create_hard_link)
create_hard_link = (T)-1;
}
@@ -1765,7 +1862,7 @@ int link(const char *oldpath, const char *newpath)
errno = ENOSYS;
return -1;
}
- if (!create_hard_link(newpath, oldpath, NULL)) {
+ if (!create_hard_link(wnewpath, woldpath, NULL)) {
errno = err_win_to_posix(GetLastError());
return -1;
}
@@ -1847,3 +1944,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();
+}
diff --git a/compat/mingw.h b/compat/mingw.h
index 3eaf822e28..510530c690 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -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
@@ -118,10 +121,7 @@ static inline int fcntl(int fd, int cmd, ...)
* simple adaptors
*/
-static inline int mingw_mkdir(const char *path, int mode)
-{
- return mkdir(path);
-}
+int mingw_mkdir(const char *path, int mode);
#define mkdir mingw_mkdir
#define WNOHANG 1
@@ -192,6 +192,19 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream);
int mingw_fflush(FILE *stream);
#define fflush mingw_fflush
+int mingw_access(const char *filename, int mode);
+#undef access
+#define access mingw_access
+
+int mingw_chdir(const char *dirname);
+#define chdir mingw_chdir
+
+int mingw_chmod(const char *filename, int mode);
+#define chmod mingw_chmod
+
+char *mingw_mktemp(char *template);
+#define mktemp mingw_mktemp
+
char *mingw_getcwd(char *pointer, int len);
#define getcwd mingw_getcwd
@@ -317,12 +330,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 +364,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 +476,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)
diff --git a/compat/win32/dirent.c b/compat/win32/dirent.c
index 7a0debe51b..52420ec7d4 100644
--- a/compat/win32/dirent.c
+++ b/compat/win32/dirent.c
@@ -1,96 +1,81 @@
-#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_DATAW *fdata)
+{
+ /* convert UTF-16 name to UTF-8 */
+ xwcstoutf(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);
+ wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
+ WIN32_FIND_DATAW fdata;
+ HANDLE h;
int len;
- DIR *p;
+ DIR *dir;
- /* check for valid path */
- if (attrs == INVALID_FILE_ATTRIBUTES) {
- errno = ENOENT;
+ /* convert name to UTF-16 and check length < MAX_PATH */
+ if ((len = xutftowcs_path(pattern, name)) < 0)
return NULL;
- }
- /* check if it's a directory */
- if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
- errno = ENOTDIR;
+ /* append optional '/' and wildcard '*' */
+ if (len && !is_dir_sep(pattern[len - 1]))
+ pattern[len++] = '/';
+ pattern[len++] = '*';
+ pattern[len] = 0;
+
+ /* open find handle */
+ h = FindFirstFileW(pattern, &fdata);
+ if (h == INVALID_HANDLE_VALUE) {
+ DWORD err = GetLastError();
+ errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
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)
- 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_DATAW fdata;
+ if (FindNextFileW(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 +86,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;
}
diff --git a/compat/win32/dirent.h b/compat/win32/dirent.h
index 927a25ca76..058207e4bf 100644
--- a/compat/win32/dirent.h
+++ b/compat/win32/dirent.h
@@ -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 * 3]; /* file name (* 3 for UTF-8 conversion) */
};
DIR *opendir(const char *dirname);
diff --git a/compat/winansi.c b/compat/winansi.c
index dedce2104e..efc5bb3a4b 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -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;
}
diff --git a/config.c b/config.c
index a1aef1cf3e..9767c4bad0 100644
--- a/config.c
+++ b/config.c
@@ -138,8 +138,7 @@ int git_config_include(const char *var, const char *value, void *data)
if (ret < 0)
return ret;
- type = skip_prefix(var, "include.");
- if (!type)
+ if (!skip_prefix(var, "include.", &type))
return ret;
if (!strcmp(type, "path"))
@@ -1637,8 +1636,8 @@ int git_config_set_multivar_in_file(const char *config_filename,
MAP_PRIVATE, in_fd, 0);
close(in_fd);
- if (fchmod(fd, st.st_mode & 07777) < 0) {
- error("fchmod on %s failed: %s",
+ if (chmod(lock->filename, st.st_mode & 07777) < 0) {
+ error("chmod on %s failed: %s",
lock->filename, strerror(errno));
ret = CONFIG_NO_WRITE;
goto out_free;
@@ -1816,8 +1815,8 @@ int git_config_rename_section_in_file(const char *config_filename,
fstat(fileno(config_file), &st);
- if (fchmod(out_fd, st.st_mode & 07777) < 0) {
- ret = error("fchmod on %s failed: %s",
+ if (chmod(lock->filename, st.st_mode & 07777) < 0) {
+ ret = error("chmod on %s failed: %s",
lock->filename, strerror(errno));
goto out;
}
diff --git a/config.mak.uname b/config.mak.uname
index 1ae675b053..00cf4c6b83 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -34,6 +34,7 @@ ifeq ($(uname_S),Linux)
HAVE_PATHS_H = YesPlease
LIBC_CONTAINS_LIBINTL = YesPlease
HAVE_DEV_TTY = YesPlease
+ HAVE_CLOCK_GETTIME = YesPlease
endif
ifeq ($(uname_S),GNU/kFreeBSD)
HAVE_ALLOCA_H = YesPlease
@@ -354,6 +355,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 +505,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 \
diff --git a/connect.c b/connect.c
index 94a6650246..5047402a1a 100644
--- a/connect.c
+++ b/connect.c
@@ -64,9 +64,7 @@ static void parse_one_symref_info(struct string_list *symref, const char *val, i
if (!len)
return; /* just "symref" */
/* e.g. "symref=HEAD:refs/heads/master" */
- sym = xmalloc(len + 1);
- memcpy(sym, val, len);
- sym[len] = '\0';
+ sym = xmemdupz(val, len);
target = strchr(sym, ':');
if (!target)
/* just "symref=something" */
@@ -129,6 +127,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
char *name;
int len, name_len;
char *buffer = packet_buffer;
+ const char *arg;
len = packet_read(in, &src_buf, &src_len,
packet_buffer, sizeof(packet_buffer),
@@ -140,12 +139,12 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
if (!len)
break;
- if (len > 4 && starts_with(buffer, "ERR "))
- die("remote error: %s", buffer + 4);
+ if (len > 4 && skip_prefix(buffer, "ERR ", &arg))
+ die("remote error: %s", arg);
- if (len == 48 && starts_with(buffer, "shallow ")) {
- if (get_sha1_hex(buffer + 8, old_sha1))
- die("protocol error: expected shallow sha-1, got '%s'", buffer + 8);
+ if (len == 48 && skip_prefix(buffer, "shallow ", &arg)) {
+ if (get_sha1_hex(arg, old_sha1))
+ die("protocol error: expected shallow sha-1, got '%s'", arg);
if (!shallow_points)
die("repository on the other end cannot be shallow");
sha1_array_append(shallow_points, old_sha1);
diff --git a/connected.c b/connected.c
index be0253e21b..dae9c9972e 100644
--- a/connected.c
+++ b/connected.c
@@ -31,6 +31,7 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
unsigned char sha1[20];
int err = 0, ac = 0;
struct packed_git *new_pack = NULL;
+ size_t base_len;
if (fn(cb_data, sha1))
return err;
@@ -38,10 +39,9 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
if (transport && transport->smart_options &&
transport->smart_options->self_contained_and_connected &&
transport->pack_lockfile &&
- ends_with(transport->pack_lockfile, ".keep")) {
+ strip_suffix(transport->pack_lockfile, ".keep", &base_len)) {
struct strbuf idx_file = STRBUF_INIT;
- strbuf_addstr(&idx_file, transport->pack_lockfile);
- strbuf_setlen(&idx_file, idx_file.len - 5); /* ".keep" */
+ strbuf_add(&idx_file, transport->pack_lockfile, base_len);
strbuf_addstr(&idx_file, ".idx");
new_pack = add_packed_git(idx_file.buf, idx_file.len, 1);
strbuf_release(&idx_file);
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 019026efcb..7a6e1d797a 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -21,6 +21,12 @@
# 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
diff --git a/contrib/convert-grafts-to-replace-refs.sh b/contrib/convert-grafts-to-replace-refs.sh
new file mode 100755
index 0000000000..0cbc917b8c
--- /dev/null
+++ b/contrib/convert-grafts-to-replace-refs.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# You should execute this script in the repository where you
+# want to convert grafts to replace refs.
+
+GRAFTS_FILE="${GIT_DIR:-.git}/info/grafts"
+
+. $(git --exec-path)/git-sh-setup
+
+test -f "$GRAFTS_FILE" || die "Could not find graft file: '$GRAFTS_FILE'"
+
+grep '^[^# ]' "$GRAFTS_FILE" |
+while read definition
+do
+ if test -n "$definition"
+ then
+ echo "Converting: $definition"
+ git replace --graft $definition ||
+ die "Conversion failed for: $definition"
+ fi
+done
+
+mv "$GRAFTS_FILE" "$GRAFTS_FILE.bak" ||
+ die "Could not rename '$GRAFTS_FILE' to '$GRAFTS_FILE.bak'"
+
+echo "Success!"
+echo "All the grafts in '$GRAFTS_FILE' have been converted to replace refs!"
+echo "The grafts file '$GRAFTS_FILE' has been renamed: '$GRAFTS_FILE.bak'"
diff --git a/contrib/examples/git-clone.sh b/contrib/examples/git-clone.sh
index b4c9376a2c..08cf246bbb 100755
--- a/contrib/examples/git-clone.sh
+++ b/contrib/examples/git-clone.sh
@@ -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
diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh
index 5cafe2eb77..934505bab9 100755
--- a/contrib/examples/git-commit.sh
+++ b/contrib/examples/git-commit.sh
@@ -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
diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh
index 7e40f40c78..52f2aafb9d 100755
--- a/contrib/examples/git-merge.sh
+++ b/contrib/examples/git-merge.sh
@@ -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
diff --git a/contrib/examples/git-repack.sh b/contrib/examples/git-repack.sh
index f312405a25..96e3fed326 100755
--- a/contrib/examples/git-repack.sh
+++ b/contrib/examples/git-repack.sh
@@ -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
diff --git a/contrib/examples/git-resolve.sh b/contrib/examples/git-resolve.sh
index 48d0fc971f..70fdc27b72 100755
--- a/contrib/examples/git-resolve.sh
+++ b/contrib/examples/git-resolve.sh
@@ -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
diff --git a/contrib/subtree/Makefile b/contrib/subtree/Makefile
index d888d45161..d9a0ce2c63 100644
--- a/contrib/subtree/Makefile
+++ b/contrib/subtree/Makefile
@@ -18,6 +18,11 @@ RM ?= rm -f
ASCIIDOC = asciidoc
XMLTO = xmlto
+ifndef SHELL_PATH
+ SHELL_PATH = /bin/sh
+endif
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
ASCIIDOC_CONF = ../../Documentation/asciidoc.conf
MANPAGE_XSL = ../../Documentation/manpage-normal.xsl
@@ -32,7 +37,8 @@ GIT_SUBTREE_HTML := git-subtree.html
all: $(GIT_SUBTREE)
$(GIT_SUBTREE): $(GIT_SUBTREE_SH)
- cp $< $@ && chmod +x $@
+ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' $< >$@
+ chmod +x $@
doc: $(GIT_SUBTREE_DOC) $(GIT_SUBTREE_HTML)
diff --git a/convert.c b/convert.c
index ab80b72357..cb5fbb45ea 100644
--- a/convert.c
+++ b/convert.c
@@ -1121,9 +1121,9 @@ static int is_foreign_ident(const char *str)
{
int i;
- if (!starts_with(str, "$Id: "))
+ if (!skip_prefix(str, "$Id: ", &str))
return 0;
- for (i = 5; str[i]; i++) {
+ for (i = 0; str[i]; i++) {
if (isspace(str[i]) && str[i+1] != '$')
return 1;
}
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 390f194252..3b370ca5e5 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -109,14 +109,12 @@ static int read_request(FILE *fh, struct credential *c,
const char *p;
strbuf_getline(&item, fh, '\n');
- p = skip_prefix(item.buf, "action=");
- if (!p)
+ if (!skip_prefix(item.buf, "action=", &p))
return error("client sent bogus action line: %s", item.buf);
strbuf_addstr(action, p);
strbuf_getline(&item, fh, '\n');
- p = skip_prefix(item.buf, "timeout=");
- if (!p)
+ if (!skip_prefix(item.buf, "timeout=", &p))
return error("client sent bogus timeout line: %s", item.buf);
*timeout = atoi(p);
diff --git a/credential.c b/credential.c
index e54753c75d..4d79d320f8 100644
--- a/credential.c
+++ b/credential.c
@@ -40,8 +40,7 @@ static int credential_config_callback(const char *var, const char *value,
struct credential *c = data;
const char *key, *dot;
- key = skip_prefix(var, "credential.");
- if (!key)
+ if (!skip_prefix(var, "credential.", &key))
return 0;
if (!value)
diff --git a/daemon.c b/daemon.c
index f9c63e9613..e6b51ed998 100644
--- a/daemon.c
+++ b/daemon.c
@@ -39,8 +39,8 @@ static int strict_paths;
static int export_all_trees;
/* Take all paths relative to this one if non-NULL */
-static char *base_path;
-static char *interpolated_path;
+static const char *base_path;
+static const char *interpolated_path;
static int base_path_relaxed;
/* Flag indicating client sent extra args. */
@@ -106,12 +106,12 @@ static void NORETURN daemon_die(const char *err, va_list params)
exit(1);
}
-static const char *path_ok(char *directory)
+static const char *path_ok(const char *directory)
{
static char rpath[PATH_MAX];
static char interp_path[PATH_MAX];
const char *path;
- char *dir;
+ const char *dir;
dir = directory;
@@ -131,7 +131,7 @@ static const char *path_ok(char *directory)
* "~alice/%s/foo".
*/
int namlen, restlen = strlen(dir);
- char *slash = strchr(dir, '/');
+ const char *slash = strchr(dir, '/');
if (!slash)
slash = dir + restlen;
namlen = slash - dir;
@@ -235,8 +235,10 @@ static int service_enabled;
static int git_daemon_config(const char *var, const char *value, void *cb)
{
- if (starts_with(var, "daemon.") &&
- !strcmp(var + 7, service_looking_at->config_name)) {
+ const char *service;
+
+ if (skip_prefix(var, "daemon.", &service) &&
+ !strcmp(service, service_looking_at->config_name)) {
service_enabled = git_config_bool(var, value);
return 0;
}
@@ -253,7 +255,7 @@ static int daemon_error(const char *dir, const char *msg)
return -1;
}
-static char *access_hook;
+static const char *access_hook;
static int run_access_hook(struct daemon_service *service, const char *dir, const char *path)
{
@@ -318,7 +320,7 @@ error_return:
return -1;
}
-static int run_service(char *dir, struct daemon_service *service)
+static int run_service(const char *dir, struct daemon_service *service)
{
const char *path;
int enabled = service->enabled;
@@ -624,15 +626,16 @@ static int execute(void)
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
struct daemon_service *s = &(daemon_service[i]);
- int namelen = strlen(s->name);
- if (starts_with(line, "git-") &&
- !strncmp(s->name, line + 4, namelen) &&
- line[namelen + 4] == ' ') {
+ const char *arg;
+
+ if (skip_prefix(line, "git-", &arg) &&
+ skip_prefix(arg, s->name, &arg) &&
+ *arg++ == ' ') {
/*
* Note: The directory here is probably context sensitive,
* and might depend on the actual service being performed.
*/
- return run_service(line + namelen + 5, s);
+ return run_service(arg, s);
}
}
@@ -775,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)
@@ -1133,16 +1135,17 @@ int main(int argc, char **argv)
for (i = 1; i < argc; i++) {
char *arg = argv[i];
+ const char *v;
- if (starts_with(arg, "--listen=")) {
- string_list_append(&listen_addr, xstrdup_tolower(arg + 9));
+ if (skip_prefix(arg, "--listen=", &v)) {
+ string_list_append(&listen_addr, xstrdup_tolower(v));
continue;
}
- if (starts_with(arg, "--port=")) {
+ if (skip_prefix(arg, "--port=", &v)) {
char *end;
unsigned long n;
- n = strtoul(arg+7, &end, 0);
- if (arg[7] && !*end) {
+ n = strtoul(v, &end, 0);
+ if (*v && !*end) {
listen_port = n;
continue;
}
@@ -1168,20 +1171,20 @@ int main(int argc, char **argv)
export_all_trees = 1;
continue;
}
- if (starts_with(arg, "--access-hook=")) {
- access_hook = arg + 14;
+ if (skip_prefix(arg, "--access-hook=", &v)) {
+ access_hook = v;
continue;
}
- if (starts_with(arg, "--timeout=")) {
- timeout = atoi(arg+10);
+ if (skip_prefix(arg, "--timeout=", &v)) {
+ timeout = atoi(v);
continue;
}
- if (starts_with(arg, "--init-timeout=")) {
- init_timeout = atoi(arg+15);
+ if (skip_prefix(arg, "--init-timeout=", &v)) {
+ init_timeout = atoi(v);
continue;
}
- if (starts_with(arg, "--max-connections=")) {
- max_connections = atoi(arg+18);
+ if (skip_prefix(arg, "--max-connections=", &v)) {
+ max_connections = atoi(v);
if (max_connections < 0)
max_connections = 0; /* unlimited */
continue;
@@ -1190,16 +1193,16 @@ int main(int argc, char **argv)
strict_paths = 1;
continue;
}
- if (starts_with(arg, "--base-path=")) {
- base_path = arg+12;
+ if (skip_prefix(arg, "--base-path=", &v)) {
+ base_path = v;
continue;
}
if (!strcmp(arg, "--base-path-relaxed")) {
base_path_relaxed = 1;
continue;
}
- if (starts_with(arg, "--interpolated-path=")) {
- interpolated_path = arg+20;
+ if (skip_prefix(arg, "--interpolated-path=", &v)) {
+ interpolated_path = v;
continue;
}
if (!strcmp(arg, "--reuseaddr")) {
@@ -1210,12 +1213,12 @@ int main(int argc, char **argv)
user_path = "";
continue;
}
- if (starts_with(arg, "--user-path=")) {
- user_path = arg + 12;
+ if (skip_prefix(arg, "--user-path=", &v)) {
+ user_path = v;
continue;
}
- if (starts_with(arg, "--pid-file=")) {
- pid_file = arg + 11;
+ if (skip_prefix(arg, "--pid-file=", &v)) {
+ pid_file = v;
continue;
}
if (!strcmp(arg, "--detach")) {
@@ -1223,28 +1226,28 @@ int main(int argc, char **argv)
log_syslog = 1;
continue;
}
- if (starts_with(arg, "--user=")) {
- user_name = arg + 7;
+ if (skip_prefix(arg, "--user=", &v)) {
+ user_name = v;
continue;
}
- if (starts_with(arg, "--group=")) {
- group_name = arg + 8;
+ if (skip_prefix(arg, "--group=", &v)) {
+ group_name = v;
continue;
}
- if (starts_with(arg, "--enable=")) {
- enable_service(arg + 9, 1);
+ if (skip_prefix(arg, "--enable=", &v)) {
+ enable_service(v, 1);
continue;
}
- if (starts_with(arg, "--disable=")) {
- enable_service(arg + 10, 0);
+ if (skip_prefix(arg, "--disable=", &v)) {
+ enable_service(v, 0);
continue;
}
- if (starts_with(arg, "--allow-override=")) {
- make_service_overridable(arg + 17, 1);
+ if (skip_prefix(arg, "--allow-override=", &v)) {
+ make_service_overridable(v, 1);
continue;
}
- if (starts_with(arg, "--forbid-override=")) {
- make_service_overridable(arg + 18, 0);
+ if (skip_prefix(arg, "--forbid-override=", &v)) {
+ make_service_overridable(v, 0);
continue;
}
if (!strcmp(arg, "--informative-errors")) {
diff --git a/decorate.c b/decorate.c
index 7cb5d29a89..b2aac90c26 100644
--- a/decorate.c
+++ b/decorate.c
@@ -8,10 +8,7 @@
static unsigned int hash_obj(const struct object *obj, unsigned int n)
{
- unsigned int hash;
-
- memcpy(&hash, obj->sha1, sizeof(unsigned int));
- return hash % n;
+ return sha1hash(obj->sha1) % n;
}
static void *insert_decoration(struct decoration *n, const struct object *base, void *decoration)
diff --git a/diff.c b/diff.c
index bba9a558a6..867f034b8f 100644
--- a/diff.c
+++ b/diff.c
@@ -52,23 +52,23 @@ static char diff_colors[][COLOR_MAXLEN] = {
GIT_COLOR_NORMAL, /* FUNCINFO */
};
-static int parse_diff_color_slot(const char *var, int ofs)
+static int parse_diff_color_slot(const char *var)
{
- if (!strcasecmp(var+ofs, "plain"))
+ if (!strcasecmp(var, "plain"))
return DIFF_PLAIN;
- if (!strcasecmp(var+ofs, "meta"))
+ if (!strcasecmp(var, "meta"))
return DIFF_METAINFO;
- if (!strcasecmp(var+ofs, "frag"))
+ if (!strcasecmp(var, "frag"))
return DIFF_FRAGINFO;
- if (!strcasecmp(var+ofs, "old"))
+ if (!strcasecmp(var, "old"))
return DIFF_FILE_OLD;
- if (!strcasecmp(var+ofs, "new"))
+ if (!strcasecmp(var, "new"))
return DIFF_FILE_NEW;
- if (!strcasecmp(var+ofs, "commit"))
+ if (!strcasecmp(var, "commit"))
return DIFF_COMMIT;
- if (!strcasecmp(var+ofs, "whitespace"))
+ if (!strcasecmp(var, "whitespace"))
return DIFF_WHITESPACE;
- if (!strcasecmp(var+ofs, "func"))
+ if (!strcasecmp(var, "func"))
return DIFF_FUNCINFO;
return -1;
}
@@ -231,6 +231,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
int git_diff_basic_config(const char *var, const char *value, void *cb)
{
+ const char *name;
+
if (!strcmp(var, "diff.renamelimit")) {
diff_rename_limit_default = git_config_int(var, value);
return 0;
@@ -239,8 +241,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
if (userdiff_config(var, value) < 0)
return -1;
- if (starts_with(var, "diff.color.") || starts_with(var, "color.diff.")) {
- int slot = parse_diff_color_slot(var, 11);
+ if (skip_prefix(var, "diff.color.", &name) ||
+ skip_prefix(var, "color.diff.", &name)) {
+ int slot = parse_diff_color_slot(name);
if (slot < 0)
return 0;
if (!value)
@@ -522,9 +525,9 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
ep += 2; /* skip over @@ */
/* The hunk header in fraginfo color */
- strbuf_add(&msgbuf, frag, strlen(frag));
+ strbuf_addstr(&msgbuf, frag);
strbuf_add(&msgbuf, line, ep - line);
- strbuf_add(&msgbuf, reset, strlen(reset));
+ strbuf_addstr(&msgbuf, reset);
/*
* trailing "\r\n"
@@ -538,15 +541,15 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
if (*ep != ' ' && *ep != '\t')
break;
if (ep != cp) {
- strbuf_add(&msgbuf, plain, strlen(plain));
+ strbuf_addstr(&msgbuf, plain);
strbuf_add(&msgbuf, cp, ep - cp);
- strbuf_add(&msgbuf, reset, strlen(reset));
+ strbuf_addstr(&msgbuf, reset);
}
if (ep < line + len) {
- strbuf_add(&msgbuf, func, strlen(func));
+ strbuf_addstr(&msgbuf, func);
strbuf_add(&msgbuf, ep, line + len - ep);
- strbuf_add(&msgbuf, reset, strlen(reset));
+ strbuf_addstr(&msgbuf, reset);
}
strbuf_add(&msgbuf, line + len, org_len - len);
@@ -2341,6 +2344,7 @@ static void builtin_diff(const char *name_a,
} else {
/* Crazy xdl interfaces.. */
const char *diffopts = getenv("GIT_DIFF_OPTS");
+ const char *v;
xpparam_t xpp;
xdemitconf_t xecfg;
struct emit_callback ecbdata;
@@ -2379,10 +2383,10 @@ static void builtin_diff(const char *name_a,
xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
if (!diffopts)
;
- else if (starts_with(diffopts, "--unified="))
- xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
- else if (starts_with(diffopts, "-u"))
- xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
+ else if (skip_prefix(diffopts, "--unified=", &v))
+ xecfg.ctxlen = strtoul(v, NULL, 10);
+ else if (skip_prefix(diffopts, "-u", &v))
+ xecfg.ctxlen = strtoul(v, NULL, 10);
if (o->word_diff)
init_diff_words_data(&ecbdata, o, one, two);
xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
@@ -3391,12 +3395,10 @@ int parse_long_opt(const char *opt, const char **argv,
const char **optarg)
{
const char *arg = argv[0];
- if (arg[0] != '-' || arg[1] != '-')
+ if (!skip_prefix(arg, "--", &arg))
return 0;
- arg += strlen("--");
- if (!starts_with(arg, opt))
+ if (!skip_prefix(arg, opt, &arg))
return 0;
- arg += strlen(opt);
if (*arg == '=') { /* stuck form: --option=value */
*optarg = arg + 1;
return 1;
@@ -3420,13 +3422,13 @@ static int stat_opt(struct diff_options *options, const char **av)
int count = options->stat_count;
int argcount = 1;
- arg += strlen("--stat");
+ if (!skip_prefix(arg, "--stat", &arg))
+ die("BUG: stat option does not begin with --stat: %s", arg);
end = (char *)arg;
switch (*arg) {
case '-':
- if (starts_with(arg, "-width")) {
- arg += strlen("-width");
+ if (skip_prefix(arg, "-width", &arg)) {
if (*arg == '=')
width = strtoul(arg + 1, &end, 10);
else if (!*arg && !av[1])
@@ -3435,8 +3437,7 @@ static int stat_opt(struct diff_options *options, const char **av)
width = strtoul(av[1], &end, 10);
argcount = 2;
}
- } else if (starts_with(arg, "-name-width")) {
- arg += strlen("-name-width");
+ } else if (skip_prefix(arg, "-name-width", &arg)) {
if (*arg == '=')
name_width = strtoul(arg + 1, &end, 10);
else if (!*arg && !av[1])
@@ -3445,8 +3446,7 @@ static int stat_opt(struct diff_options *options, const char **av)
name_width = strtoul(av[1], &end, 10);
argcount = 2;
}
- } else if (starts_with(arg, "-graph-width")) {
- arg += strlen("-graph-width");
+ } else if (skip_prefix(arg, "-graph-width", &arg)) {
if (*arg == '=')
graph_width = strtoul(arg + 1, &end, 10);
else if (!*arg && !av[1])
@@ -3455,8 +3455,7 @@ static int stat_opt(struct diff_options *options, const char **av)
graph_width = strtoul(av[1], &end, 10);
argcount = 2;
}
- } else if (starts_with(arg, "-count")) {
- arg += strlen("-count");
+ } else if (skip_prefix(arg, "-count", &arg)) {
if (*arg == '=')
count = strtoul(arg + 1, &end, 10);
else if (!*arg && !av[1])
@@ -3609,17 +3608,17 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->output_format |= DIFF_FORMAT_SHORTSTAT;
else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
return parse_dirstat_opt(options, "");
- else if (starts_with(arg, "-X"))
- return parse_dirstat_opt(options, arg + 2);
- else if (starts_with(arg, "--dirstat="))
- return parse_dirstat_opt(options, arg + 10);
+ else if (skip_prefix(arg, "-X", &arg))
+ return parse_dirstat_opt(options, arg);
+ else if (skip_prefix(arg, "--dirstat=", &arg))
+ return parse_dirstat_opt(options, arg);
else if (!strcmp(arg, "--cumulative"))
return parse_dirstat_opt(options, "cumulative");
else if (!strcmp(arg, "--dirstat-by-file"))
return parse_dirstat_opt(options, "files");
- else if (starts_with(arg, "--dirstat-by-file=")) {
+ else if (skip_prefix(arg, "--dirstat-by-file=", &arg)) {
parse_dirstat_opt(options, "files");
- return parse_dirstat_opt(options, arg + 18);
+ return parse_dirstat_opt(options, arg);
}
else if (!strcmp(arg, "--check"))
options->output_format |= DIFF_FORMAT_CHECKDIFF;
@@ -3669,9 +3668,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
DIFF_OPT_CLR(options, RENAME_EMPTY);
else if (!strcmp(arg, "--relative"))
DIFF_OPT_SET(options, RELATIVE_NAME);
- else if (starts_with(arg, "--relative=")) {
+ else if (skip_prefix(arg, "--relative=", &arg)) {
DIFF_OPT_SET(options, RELATIVE_NAME);
- options->prefix = arg + 11;
+ options->prefix = arg;
}
/* xdiff options */
@@ -3722,8 +3721,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
DIFF_OPT_CLR(options, FOLLOW_RENAMES);
else if (!strcmp(arg, "--color"))
options->use_color = 1;
- else if (starts_with(arg, "--color=")) {
- int value = git_config_colorbool(NULL, arg+8);
+ else if (skip_prefix(arg, "--color=", &arg)) {
+ int value = git_config_colorbool(NULL, arg);
if (value < 0)
return error("option `color' expects \"always\", \"auto\", or \"never\"");
options->use_color = value;
@@ -3734,29 +3733,28 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->use_color = 1;
options->word_diff = DIFF_WORDS_COLOR;
}
- else if (starts_with(arg, "--color-words=")) {
+ else if (skip_prefix(arg, "--color-words=", &arg)) {
options->use_color = 1;
options->word_diff = DIFF_WORDS_COLOR;
- options->word_regex = arg + 14;
+ options->word_regex = arg;
}
else if (!strcmp(arg, "--word-diff")) {
if (options->word_diff == DIFF_WORDS_NONE)
options->word_diff = DIFF_WORDS_PLAIN;
}
- else if (starts_with(arg, "--word-diff=")) {
- const char *type = arg + 12;
- if (!strcmp(type, "plain"))
+ else if (skip_prefix(arg, "--word-diff=", &arg)) {
+ if (!strcmp(arg, "plain"))
options->word_diff = DIFF_WORDS_PLAIN;
- else if (!strcmp(type, "color")) {
+ else if (!strcmp(arg, "color")) {
options->use_color = 1;
options->word_diff = DIFF_WORDS_COLOR;
}
- else if (!strcmp(type, "porcelain"))
+ else if (!strcmp(arg, "porcelain"))
options->word_diff = DIFF_WORDS_PORCELAIN;
- else if (!strcmp(type, "none"))
+ else if (!strcmp(arg, "none"))
options->word_diff = DIFF_WORDS_NONE;
else
- die("bad --word-diff argument: %s", type);
+ die("bad --word-diff argument: %s", arg);
}
else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {
if (options->word_diff == DIFF_WORDS_NONE)
@@ -3779,13 +3777,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
else if (!strcmp(arg, "--ignore-submodules")) {
DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(options, "all");
- } else if (starts_with(arg, "--ignore-submodules=")) {
+ } else if (skip_prefix(arg, "--ignore-submodules=", &arg)) {
DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
- handle_ignore_submodules_arg(options, arg + 20);
+ handle_ignore_submodules_arg(options, arg);
} else if (!strcmp(arg, "--submodule"))
DIFF_OPT_SET(options, SUBMODULE_LOG);
- else if (starts_with(arg, "--submodule="))
- return parse_submodule_opt(options, arg + 12);
+ else if (skip_prefix(arg, "--submodule=", &arg))
+ return parse_submodule_opt(options, arg);
/* misc options */
else if (!strcmp(arg, "-z"))
@@ -3820,8 +3818,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
}
else if (!strcmp(arg, "--abbrev"))
options->abbrev = DEFAULT_ABBREV;
- else if (starts_with(arg, "--abbrev=")) {
- options->abbrev = strtoul(arg + 9, NULL, 10);
+ else if (skip_prefix(arg, "--abbrev=", &arg)) {
+ options->abbrev = strtoul(arg, NULL, 10);
if (options->abbrev < MINIMUM_ABBREV)
options->abbrev = MINIMUM_ABBREV;
else if (40 < options->abbrev)
@@ -3902,16 +3900,13 @@ static int diff_scoreopt_parse(const char *opt)
cmd = *opt++;
if (cmd == '-') {
/* convert the long-form arguments into short-form versions */
- if (starts_with(opt, "break-rewrites")) {
- opt += strlen("break-rewrites");
+ if (skip_prefix(opt, "break-rewrites", &opt)) {
if (*opt == 0 || *opt++ == '=')
cmd = 'B';
- } else if (starts_with(opt, "find-copies")) {
- opt += strlen("find-copies");
+ } else if (skip_prefix(opt, "find-copies", &opt)) {
if (*opt == 0 || *opt++ == '=')
cmd = 'C';
- } else if (starts_with(opt, "find-renames")) {
- opt += strlen("find-renames");
+ } else if (skip_prefix(opt, "find-renames", &opt)) {
if (*opt == 0 || *opt++ == '=')
cmd = 'M';
}
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 749a35d2c2..2e44a37459 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -242,14 +242,12 @@ struct file_similarity {
static unsigned int hash_filespec(struct diff_filespec *filespec)
{
- unsigned int hash;
if (!filespec->sha1_valid) {
if (diff_populate_filespec(filespec, 0))
return 0;
hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1);
}
- memcpy(&hash, filespec->sha1, sizeof(hash));
- return hash;
+ return sha1hash(filespec->sha1);
}
static int find_identical_files(struct hashmap *srcs,
@@ -259,15 +257,14 @@ static int find_identical_files(struct hashmap *srcs,
int renames = 0;
struct diff_filespec *target = rename_dst[dst_index].two;
- struct file_similarity *p, *best, dst;
+ struct file_similarity *p, *best = NULL;
int i = 100, best_score = -1;
/*
* Find the best source match for specified destination.
*/
- best = NULL;
- hashmap_entry_init(&dst, hash_filespec(target));
- for (p = hashmap_get(srcs, &dst, NULL); p; p = hashmap_get_next(srcs, p)) {
+ p = hashmap_get_from_hash(srcs, hash_filespec(target), NULL);
+ for (; p; p = hashmap_get_next(srcs, p)) {
int score;
struct diff_filespec *source = p->filespec;
diff --git a/dir.c b/dir.c
index 797805d6a1..fcb68729b1 100644
--- a/dir.c
+++ b/dir.c
@@ -557,8 +557,7 @@ int add_excludes_from_file_to_list(const char *fname,
buf = xrealloc(buf, size+1);
buf[size++] = '\n';
}
- }
- else {
+ } else {
size = xsize_t(st.st_size);
if (size == 0) {
close(fd);
@@ -793,17 +792,19 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
group = &dir->exclude_list_group[EXC_DIRS];
- /* Pop the exclude lists from the EXCL_DIRS exclude_list_group
+ /*
+ * Pop the exclude lists from the EXCL_DIRS exclude_list_group
* which originate from directories not in the prefix of the
- * path being checked. */
+ * path being checked.
+ */
while ((stk = dir->exclude_stack) != NULL) {
if (stk->baselen <= baselen &&
- !strncmp(dir->basebuf, base, stk->baselen))
+ !strncmp(dir->basebuf.buf, base, stk->baselen))
break;
el = &group->el[dir->exclude_stack->exclude_ix];
dir->exclude_stack = stk->prev;
dir->exclude = NULL;
- free((char *)el->src); /* see strdup() below */
+ free((char *)el->src); /* see strbuf_detach() below */
clear_exclude_list(el);
free(stk);
group->nr--;
@@ -813,8 +814,17 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
if (dir->exclude)
return;
+ /*
+ * Lazy initialization. All call sites currently just
+ * memset(dir, 0, sizeof(*dir)) before use. Changing all of
+ * them seems lots of work for little benefit.
+ */
+ if (!dir->basebuf.buf)
+ strbuf_init(&dir->basebuf, PATH_MAX);
+
/* Read from the parent directories and push them down. */
current = stk ? stk->baselen : -1;
+ strbuf_setlen(&dir->basebuf, current < 0 ? 0 : current);
while (current < baselen) {
struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
const char *cp;
@@ -822,8 +832,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
if (current < 0) {
cp = base;
current = 0;
- }
- else {
+ } else {
cp = strchr(base + current + 1, '/');
if (!cp)
die("oops in prep_exclude");
@@ -833,48 +842,47 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
stk->baselen = cp - base;
stk->exclude_ix = group->nr;
el = add_exclude_list(dir, EXC_DIRS, NULL);
- memcpy(dir->basebuf + current, base + current,
- stk->baselen - current);
+ strbuf_add(&dir->basebuf, base + current, stk->baselen - current);
+ assert(stk->baselen == dir->basebuf.len);
/* Abort if the directory is excluded */
if (stk->baselen) {
int dt = DT_DIR;
- dir->basebuf[stk->baselen - 1] = 0;
+ dir->basebuf.buf[stk->baselen - 1] = 0;
dir->exclude = last_exclude_matching_from_lists(dir,
- dir->basebuf, stk->baselen - 1,
- dir->basebuf + current, &dt);
- dir->basebuf[stk->baselen - 1] = '/';
+ dir->basebuf.buf, stk->baselen - 1,
+ dir->basebuf.buf + current, &dt);
+ dir->basebuf.buf[stk->baselen - 1] = '/';
if (dir->exclude &&
dir->exclude->flags & EXC_FLAG_NEGATIVE)
dir->exclude = NULL;
if (dir->exclude) {
- dir->basebuf[stk->baselen] = 0;
dir->exclude_stack = stk;
return;
}
}
- /* Try to read per-directory file unless path is too long */
- if (dir->exclude_per_dir &&
- stk->baselen + strlen(dir->exclude_per_dir) < PATH_MAX) {
- strcpy(dir->basebuf + stk->baselen,
- dir->exclude_per_dir);
+ /* Try to read per-directory file */
+ if (dir->exclude_per_dir) {
/*
* dir->basebuf gets reused by the traversal, but we
* need fname to remain unchanged to ensure the src
* member of each struct exclude correctly
* back-references its source file. Other invocations
* of add_exclude_list provide stable strings, so we
- * strdup() and free() here in the caller.
+ * strbuf_detach() and free() here in the caller.
*/
- el->src = strdup(dir->basebuf);
- add_excludes_from_file_to_list(dir->basebuf,
- dir->basebuf, stk->baselen, el, 1);
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addbuf(&sb, &dir->basebuf);
+ strbuf_addstr(&sb, dir->exclude_per_dir);
+ el->src = strbuf_detach(&sb, NULL);
+ add_excludes_from_file_to_list(el->src, el->src,
+ stk->baselen, el, 1);
}
dir->exclude_stack = stk;
current = stk->baselen;
}
- dir->basebuf[baselen] = '\0';
+ strbuf_setlen(&dir->basebuf, baselen);
}
/*
@@ -1354,8 +1362,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)
@@ -1672,4 +1679,5 @@ void clear_directory(struct dir_struct *dir)
free(stk);
stk = prev;
}
+ strbuf_release(&dir->basebuf);
}
diff --git a/dir.h b/dir.h
index 55e53456af..6c45e9d4b9 100644
--- a/dir.h
+++ b/dir.h
@@ -15,6 +15,27 @@ struct dir_entry {
#define EXC_FLAG_MUSTBEDIR 8
#define EXC_FLAG_NEGATIVE 16
+struct exclude {
+ /*
+ * This allows callers of last_exclude_matching() etc.
+ * to determine the origin of the matching pattern.
+ */
+ struct exclude_list *el;
+
+ const char *pattern;
+ int patternlen;
+ int nowildcardlen;
+ const char *base;
+ int baselen;
+ int flags;
+
+ /*
+ * Counting starts from 1 for line numbers in ignore files,
+ * and from -1 decrementing for patterns from CLI args.
+ */
+ int srcpos;
+};
+
/*
* Each excludes file will be parsed into a fresh exclude_list which
* is appended to the relevant exclude_list_group (either EXC_DIRS or
@@ -32,26 +53,7 @@ struct exclude_list {
/* origin of list, e.g. path to filename, or descriptive string */
const char *src;
- struct exclude {
- /*
- * This allows callers of last_exclude_matching() etc.
- * to determine the origin of the matching pattern.
- */
- struct exclude_list *el;
-
- const char *pattern;
- int patternlen;
- int nowildcardlen;
- const char *base;
- int baselen;
- int flags;
-
- /*
- * Counting starts from 1 for line numbers in ignore files,
- * and from -1 decrementing for patterns from CLI args.
- */
- int srcpos;
- } **excludes;
+ struct exclude **excludes;
};
/*
@@ -117,7 +119,7 @@ struct dir_struct {
*/
struct exclude_stack *exclude_stack;
struct exclude *exclude;
- char basebuf[PATH_MAX];
+ struct strbuf basebuf;
};
/*
diff --git a/entry.c b/entry.c
index 77c6882624..1eda8e9471 100644
--- 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;
}
diff --git a/environment.c b/environment.c
index 4dac5e9edd..565f65293b 100644
--- a/environment.c
+++ b/environment.c
@@ -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));
diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c
index f7f700ef51..1c2d7afd4c 100644
--- a/ewah/ewah_io.c
+++ b/ewah/ewah_io.c
@@ -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);
diff --git a/ewah/ewok.h b/ewah/ewok.h
index 43adeb5c68..f6ad190a03 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -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);
diff --git a/fast-import.c b/fast-import.c
index 6707a66471..d73f58cbe3 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -371,8 +371,8 @@ static volatile sig_atomic_t checkpoint_requested;
static int cat_blob_fd = STDOUT_FILENO;
static void parse_argv(void);
-static void parse_cat_blob(void);
-static void parse_ls(struct branch *b);
+static void parse_cat_blob(const char *p);
+static void parse_ls(const char *p, struct branch *b);
static void write_branch_report(FILE *rpt, struct branch *b)
{
@@ -1861,6 +1861,8 @@ static int read_next_command(void)
}
for (;;) {
+ const char *p;
+
if (unread_command_buf) {
unread_command_buf = 0;
} else {
@@ -1893,8 +1895,8 @@ static int read_next_command(void)
rc->prev->next = rc;
cmd_tail = rc;
}
- if (starts_with(command_buf.buf, "cat-blob ")) {
- parse_cat_blob();
+ if (skip_prefix(command_buf.buf, "cat-blob ", &p)) {
+ parse_cat_blob(p);
continue;
}
if (command_buf.buf[0] == '#')
@@ -1912,8 +1914,9 @@ static void skip_optional_lf(void)
static void parse_mark(void)
{
- if (starts_with(command_buf.buf, "mark :")) {
- next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
+ const char *v;
+ if (skip_prefix(command_buf.buf, "mark :", &v)) {
+ next_mark = strtoumax(v, NULL, 10);
read_next_command();
}
else
@@ -1922,14 +1925,15 @@ static void parse_mark(void)
static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
{
+ const char *data;
strbuf_reset(sb);
- if (!starts_with(command_buf.buf, "data "))
+ if (!skip_prefix(command_buf.buf, "data ", &data))
die("Expected 'data n' command, found: %s", command_buf.buf);
- if (starts_with(command_buf.buf + 5, "<<")) {
- char *term = xstrdup(command_buf.buf + 5 + 2);
- size_t term_len = command_buf.len - 5 - 2;
+ if (skip_prefix(data, "<<", &data)) {
+ char *term = xstrdup(data);
+ size_t term_len = command_buf.len - (data - command_buf.buf);
strbuf_detach(&command_buf, NULL);
for (;;) {
@@ -1944,7 +1948,7 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
free(term);
}
else {
- uintmax_t len = strtoumax(command_buf.buf + 5, NULL, 10);
+ uintmax_t len = strtoumax(data, NULL, 10);
size_t n = 0, length = (size_t)len;
if (limit && limit < len) {
@@ -2265,15 +2269,14 @@ static uintmax_t parse_mark_ref_space(const char **p)
char *end;
mark = parse_mark_ref(*p, &end);
- if (*end != ' ')
+ if (*end++ != ' ')
die("Missing space after mark: %s", command_buf.buf);
*p = end;
return mark;
}
-static void file_change_m(struct branch *b)
+static void file_change_m(const char *p, struct branch *b)
{
- const char *p = command_buf.buf + 2;
static struct strbuf uq = STRBUF_INIT;
const char *endp;
struct object_entry *oe;
@@ -2301,20 +2304,17 @@ static void file_change_m(struct branch *b)
if (*p == ':') {
oe = find_mark(parse_mark_ref_space(&p));
hashcpy(sha1, oe->idx.sha1);
- } else if (starts_with(p, "inline ")) {
+ } else if (skip_prefix(p, "inline ", &p)) {
inline_data = 1;
oe = NULL; /* not used with inline_data, but makes gcc happy */
- p += strlen("inline"); /* advance to space */
} else {
if (get_sha1_hex(p, sha1))
die("Invalid dataref: %s", command_buf.buf);
oe = find_object(sha1);
p += 40;
- if (*p != ' ')
+ if (*p++ != ' ')
die("Missing space after SHA1: %s", command_buf.buf);
}
- assert(*p == ' ');
- p++; /* skip space */
strbuf_reset(&uq);
if (!unquote_c_style(&uq, p, &endp)) {
@@ -2324,7 +2324,7 @@ static void file_change_m(struct branch *b)
}
/* Git does not track empty, non-toplevel directories. */
- if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) {
+ if (S_ISDIR(mode) && !hashcmp(sha1, EMPTY_TREE_SHA1_BIN) && *p) {
tree_content_remove(&b->branch_tree, p, NULL, 0);
return;
}
@@ -2374,9 +2374,8 @@ static void file_change_m(struct branch *b)
tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
}
-static void file_change_d(struct branch *b)
+static void file_change_d(const char *p, struct branch *b)
{
- const char *p = command_buf.buf + 2;
static struct strbuf uq = STRBUF_INIT;
const char *endp;
@@ -2389,15 +2388,14 @@ static void file_change_d(struct branch *b)
tree_content_remove(&b->branch_tree, p, NULL, 1);
}
-static void file_change_cr(struct branch *b, int rename)
+static void file_change_cr(const char *s, struct branch *b, int rename)
{
- const char *s, *d;
+ const char *d;
static struct strbuf s_uq = STRBUF_INIT;
static struct strbuf d_uq = STRBUF_INIT;
const char *endp;
struct tree_entry leaf;
- s = command_buf.buf + 2;
strbuf_reset(&s_uq);
if (!unquote_c_style(&s_uq, s, &endp)) {
if (*endp != ' ')
@@ -2442,9 +2440,8 @@ static void file_change_cr(struct branch *b, int rename)
leaf.tree);
}
-static void note_change_n(struct branch *b, unsigned char *old_fanout)
+static void note_change_n(const char *p, struct branch *b, unsigned char *old_fanout)
{
- const char *p = command_buf.buf + 2;
static struct strbuf uq = STRBUF_INIT;
struct object_entry *oe;
struct branch *s;
@@ -2474,20 +2471,17 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout)
if (*p == ':') {
oe = find_mark(parse_mark_ref_space(&p));
hashcpy(sha1, oe->idx.sha1);
- } else if (starts_with(p, "inline ")) {
+ } else if (skip_prefix(p, "inline ", &p)) {
inline_data = 1;
oe = NULL; /* not used with inline_data, but makes gcc happy */
- p += strlen("inline"); /* advance to space */
} else {
if (get_sha1_hex(p, sha1))
die("Invalid dataref: %s", command_buf.buf);
oe = find_object(sha1);
p += 40;
- if (*p != ' ')
+ if (*p++ != ' ')
die("Missing space after SHA1: %s", command_buf.buf);
}
- assert(*p == ' ');
- p++; /* skip space */
/* <commit-ish> */
s = lookup_branch(p);
@@ -2585,7 +2579,7 @@ static int parse_from(struct branch *b)
const char *from;
struct branch *s;
- if (!starts_with(command_buf.buf, "from "))
+ if (!skip_prefix(command_buf.buf, "from ", &from))
return 0;
if (b->branch_tree.tree) {
@@ -2593,7 +2587,6 @@ static int parse_from(struct branch *b)
b->branch_tree.tree = NULL;
}
- from = strchr(command_buf.buf, ' ') + 1;
s = lookup_branch(from);
if (b == s)
die("Can't create a branch from itself: %s", b->name);
@@ -2634,8 +2627,7 @@ static struct hash_list *parse_merge(unsigned int *count)
struct branch *s;
*count = 0;
- while (starts_with(command_buf.buf, "merge ")) {
- from = strchr(command_buf.buf, ' ') + 1;
+ while (skip_prefix(command_buf.buf, "merge ", &from)) {
n = xmalloc(sizeof(*n));
s = lookup_branch(from);
if (s)
@@ -2666,31 +2658,29 @@ static struct hash_list *parse_merge(unsigned int *count)
return list;
}
-static void parse_new_commit(void)
+static void parse_new_commit(const char *arg)
{
static struct strbuf msg = STRBUF_INIT;
struct branch *b;
- char *sp;
char *author = NULL;
char *committer = NULL;
struct hash_list *merge_list = NULL;
unsigned int merge_count;
unsigned char prev_fanout, new_fanout;
+ const char *v;
- /* Obtain the branch name from the rest of our command */
- sp = strchr(command_buf.buf, ' ') + 1;
- b = lookup_branch(sp);
+ b = lookup_branch(arg);
if (!b)
- b = new_branch(sp);
+ b = new_branch(arg);
read_next_command();
parse_mark();
- if (starts_with(command_buf.buf, "author ")) {
- author = parse_ident(command_buf.buf + 7);
+ if (skip_prefix(command_buf.buf, "author ", &v)) {
+ author = parse_ident(v);
read_next_command();
}
- if (starts_with(command_buf.buf, "committer ")) {
- committer = parse_ident(command_buf.buf + 10);
+ if (skip_prefix(command_buf.buf, "committer ", &v)) {
+ committer = parse_ident(v);
read_next_command();
}
if (!committer)
@@ -2710,20 +2700,20 @@ static void parse_new_commit(void)
/* file_change* */
while (command_buf.len > 0) {
- if (starts_with(command_buf.buf, "M "))
- file_change_m(b);
- else if (starts_with(command_buf.buf, "D "))
- file_change_d(b);
- else if (starts_with(command_buf.buf, "R "))
- file_change_cr(b, 1);
- else if (starts_with(command_buf.buf, "C "))
- file_change_cr(b, 0);
- else if (starts_with(command_buf.buf, "N "))
- note_change_n(b, &prev_fanout);
+ if (skip_prefix(command_buf.buf, "M ", &v))
+ file_change_m(v, b);
+ else if (skip_prefix(command_buf.buf, "D ", &v))
+ file_change_d(v, b);
+ else if (skip_prefix(command_buf.buf, "R ", &v))
+ file_change_cr(v, b, 1);
+ else if (skip_prefix(command_buf.buf, "C ", &v))
+ file_change_cr(v, b, 0);
+ else if (skip_prefix(command_buf.buf, "N ", &v))
+ note_change_n(v, b, &prev_fanout);
else if (!strcmp("deleteall", command_buf.buf))
file_change_deleteall(b);
- else if (starts_with(command_buf.buf, "ls "))
- parse_ls(b);
+ else if (skip_prefix(command_buf.buf, "ls ", &v))
+ parse_ls(v, b);
else {
unread_command_buf = 1;
break;
@@ -2766,10 +2756,9 @@ static void parse_new_commit(void)
b->last_commit = object_count_by_type[OBJ_COMMIT];
}
-static void parse_new_tag(void)
+static void parse_new_tag(const char *arg)
{
static struct strbuf msg = STRBUF_INIT;
- char *sp;
const char *from;
char *tagger;
struct branch *s;
@@ -2777,12 +2766,11 @@ static void parse_new_tag(void)
uintmax_t from_mark = 0;
unsigned char sha1[20];
enum object_type type;
+ const char *v;
- /* Obtain the new tag name from the rest of our command */
- sp = strchr(command_buf.buf, ' ') + 1;
t = pool_alloc(sizeof(struct tag));
memset(t, 0, sizeof(struct tag));
- t->name = pool_strdup(sp);
+ t->name = pool_strdup(arg);
if (last_tag)
last_tag->next_tag = t;
else
@@ -2791,9 +2779,8 @@ static void parse_new_tag(void)
read_next_command();
/* from ... */
- if (!starts_with(command_buf.buf, "from "))
+ if (!skip_prefix(command_buf.buf, "from ", &from))
die("Expected from command, got %s", command_buf.buf);
- from = strchr(command_buf.buf, ' ') + 1;
s = lookup_branch(from);
if (s) {
if (is_null_sha1(s->sha1))
@@ -2819,8 +2806,8 @@ static void parse_new_tag(void)
read_next_command();
/* tagger ... */
- if (starts_with(command_buf.buf, "tagger ")) {
- tagger = parse_ident(command_buf.buf + 7);
+ if (skip_prefix(command_buf.buf, "tagger ", &v)) {
+ tagger = parse_ident(v);
read_next_command();
} else
tagger = NULL;
@@ -2849,14 +2836,11 @@ static void parse_new_tag(void)
t->pack_id = pack_id;
}
-static void parse_reset_branch(void)
+static void parse_reset_branch(const char *arg)
{
struct branch *b;
- char *sp;
- /* Obtain the branch name from the rest of our command */
- sp = strchr(command_buf.buf, ' ') + 1;
- b = lookup_branch(sp);
+ b = lookup_branch(arg);
if (b) {
hashclr(b->sha1);
hashclr(b->branch_tree.versions[0].sha1);
@@ -2867,7 +2851,7 @@ static void parse_reset_branch(void)
}
}
else
- b = new_branch(sp);
+ b = new_branch(arg);
read_next_command();
parse_from(b);
if (command_buf.len > 0)
@@ -2925,14 +2909,12 @@ static void cat_blob(struct object_entry *oe, unsigned char sha1[20])
free(buf);
}
-static void parse_cat_blob(void)
+static void parse_cat_blob(const char *p)
{
- const char *p;
struct object_entry *oe = oe;
unsigned char sha1[20];
/* cat-blob SP <object> LF */
- p = command_buf.buf + strlen("cat-blob ");
if (*p == ':') {
oe = find_mark(parse_mark_ref_eol(p));
if (!oe)
@@ -3015,6 +2997,8 @@ static struct object_entry *parse_treeish_dataref(const char **p)
die("Invalid dataref: %s", command_buf.buf);
e = find_object(sha1);
*p += 40;
+ if (*(*p)++ != ' ')
+ die("Missing space after tree-ish: %s", command_buf.buf);
}
while (!e || e->type != OBJ_TREE)
@@ -3049,14 +3033,12 @@ static void print_ls(int mode, const unsigned char *sha1, const char *path)
cat_blob_write(line.buf, line.len);
}
-static void parse_ls(struct branch *b)
+static void parse_ls(const char *p, struct branch *b)
{
- const char *p;
struct tree_entry *root = NULL;
struct tree_entry leaf = {NULL};
/* ls SP (<tree-ish> SP)? <path> */
- p = command_buf.buf + strlen("ls ");
if (*p == '"') {
if (!b)
die("Not in a commit: %s", command_buf.buf);
@@ -3068,8 +3050,6 @@ static void parse_ls(struct branch *b)
if (!is_null_sha1(root->versions[1].sha1))
root->versions[1].mode = S_IFDIR;
load_tree(root);
- if (*p++ != ' ')
- die("Missing space after tree-ish: %s", command_buf.buf);
}
if (*p == '"') {
static struct strbuf uq = STRBUF_INIT;
@@ -3207,9 +3187,9 @@ static void option_export_pack_edges(const char *edges)
static int parse_one_option(const char *option)
{
- if (starts_with(option, "max-pack-size=")) {
+ if (skip_prefix(option, "max-pack-size=", &option)) {
unsigned long v;
- if (!git_parse_ulong(option + 14, &v))
+ if (!git_parse_ulong(option, &v))
return 0;
if (v < 8192) {
warning("max-pack-size is now in bytes, assuming --max-pack-size=%lum", v);
@@ -3219,17 +3199,17 @@ static int parse_one_option(const char *option)
v = 1024 * 1024;
}
max_packsize = v;
- } else if (starts_with(option, "big-file-threshold=")) {
+ } else if (skip_prefix(option, "big-file-threshold=", &option)) {
unsigned long v;
- if (!git_parse_ulong(option + 19, &v))
+ if (!git_parse_ulong(option, &v))
return 0;
big_file_threshold = v;
- } else if (starts_with(option, "depth=")) {
- option_depth(option + 6);
- } else if (starts_with(option, "active-branches=")) {
- option_active_branches(option + 16);
- } else if (starts_with(option, "export-pack-edges=")) {
- option_export_pack_edges(option + 18);
+ } else if (skip_prefix(option, "depth=", &option)) {
+ option_depth(option);
+ } else if (skip_prefix(option, "active-branches=", &option)) {
+ option_active_branches(option);
+ } else if (skip_prefix(option, "export-pack-edges=", &option)) {
+ option_export_pack_edges(option);
} else if (starts_with(option, "quiet")) {
show_stats = 0;
} else if (starts_with(option, "stats")) {
@@ -3243,15 +3223,16 @@ static int parse_one_option(const char *option)
static int parse_one_feature(const char *feature, int from_stream)
{
- if (starts_with(feature, "date-format=")) {
- option_date_format(feature + 12);
- } else if (starts_with(feature, "import-marks=")) {
- option_import_marks(feature + 13, from_stream, 0);
- } else if (starts_with(feature, "import-marks-if-exists=")) {
- option_import_marks(feature + strlen("import-marks-if-exists="),
- from_stream, 1);
- } else if (starts_with(feature, "export-marks=")) {
- option_export_marks(feature + 13);
+ const char *arg;
+
+ if (skip_prefix(feature, "date-format=", &arg)) {
+ option_date_format(arg);
+ } else if (skip_prefix(feature, "import-marks=", &arg)) {
+ option_import_marks(arg, from_stream, 0);
+ } else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) {
+ option_import_marks(arg, from_stream, 1);
+ } else if (skip_prefix(feature, "export-marks=", &arg)) {
+ option_export_marks(arg);
} else if (!strcmp(feature, "cat-blob")) {
; /* Don't die - this feature is supported */
} else if (!strcmp(feature, "relative-marks")) {
@@ -3271,10 +3252,8 @@ static int parse_one_feature(const char *feature, int from_stream)
return 1;
}
-static void parse_feature(void)
+static void parse_feature(const char *feature)
{
- char *feature = command_buf.buf + 8;
-
if (seen_data_command)
die("Got feature command '%s' after data command", feature);
@@ -3284,10 +3263,8 @@ static void parse_feature(void)
die("This version of fast-import does not support feature %s.", feature);
}
-static void parse_option(void)
+static void parse_option(const char *option)
{
- char *option = command_buf.buf + 11;
-
if (seen_data_command)
die("Got option command '%s' after data command", option);
@@ -3342,18 +3319,21 @@ static void parse_argv(void)
if (*a != '-' || !strcmp(a, "--"))
break;
- if (parse_one_option(a + 2))
+ if (!skip_prefix(a, "--", &a))
+ die("unknown option %s", a);
+
+ if (parse_one_option(a))
continue;
- if (parse_one_feature(a + 2, 0))
+ if (parse_one_feature(a, 0))
continue;
- if (starts_with(a + 2, "cat-blob-fd=")) {
- option_cat_blob_fd(a + 2 + strlen("cat-blob-fd="));
+ if (skip_prefix(a, "cat-blob-fd=", &a)) {
+ option_cat_blob_fd(a);
continue;
}
- die("unknown option %s", a);
+ die("unknown option --%s", a);
}
if (i != global_argc)
usage(fast_import_usage);
@@ -3400,26 +3380,27 @@ int main(int argc, char **argv)
set_die_routine(die_nicely);
set_checkpoint_signal();
while (read_next_command() != EOF) {
+ const char *v;
if (!strcmp("blob", command_buf.buf))
parse_new_blob();
- else if (starts_with(command_buf.buf, "ls "))
- parse_ls(NULL);
- else if (starts_with(command_buf.buf, "commit "))
- parse_new_commit();
- else if (starts_with(command_buf.buf, "tag "))
- parse_new_tag();
- else if (starts_with(command_buf.buf, "reset "))
- parse_reset_branch();
+ else if (skip_prefix(command_buf.buf, "ls ", &v))
+ parse_ls(v, NULL);
+ else if (skip_prefix(command_buf.buf, "commit ", &v))
+ parse_new_commit(v);
+ else if (skip_prefix(command_buf.buf, "tag ", &v))
+ parse_new_tag(v);
+ else if (skip_prefix(command_buf.buf, "reset ", &v))
+ parse_reset_branch(v);
else if (!strcmp("checkpoint", command_buf.buf))
parse_checkpoint();
else if (!strcmp("done", command_buf.buf))
break;
else if (starts_with(command_buf.buf, "progress "))
parse_progress();
- else if (starts_with(command_buf.buf, "feature "))
- parse_feature();
- else if (starts_with(command_buf.buf, "option git "))
- parse_option();
+ else if (skip_prefix(command_buf.buf, "feature ", &v))
+ parse_feature(v);
+ else if (skip_prefix(command_buf.buf, "option git ", &v))
+ parse_option(v);
else if (starts_with(command_buf.buf, "option "))
/* ignore non-git options*/;
else
diff --git a/fetch-pack.c b/fetch-pack.c
index b12bd4c59a..b8a58fa7a5 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -189,20 +189,23 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
{
int len;
char *line = packet_read_line(fd, &len);
+ const char *arg;
if (!len)
die("git fetch-pack: expected ACK/NAK, got EOF");
if (!strcmp(line, "NAK"))
return NAK;
- if (starts_with(line, "ACK ")) {
- if (!get_sha1_hex(line+4, result_sha1)) {
- if (len < 45)
+ if (skip_prefix(line, "ACK ", &arg)) {
+ if (!get_sha1_hex(arg, result_sha1)) {
+ arg += 40;
+ len -= arg - line;
+ if (len < 1)
return ACK;
- if (strstr(line+45, "continue"))
+ if (strstr(arg, "continue"))
return ACK_continue;
- if (strstr(line+45, "common"))
+ if (strstr(arg, "common"))
return ACK_common;
- if (strstr(line+45, "ready"))
+ if (strstr(arg, "ready"))
return ACK_ready;
return ACK;
}
@@ -319,18 +322,19 @@ static int find_common(struct fetch_pack_args *args,
if (args->depth > 0) {
char *line;
+ const char *arg;
unsigned char sha1[20];
send_request(args, fd[1], &req_buf);
while ((line = packet_read_line(fd[0], NULL))) {
- if (starts_with(line, "shallow ")) {
- if (get_sha1_hex(line + 8, sha1))
+ if (skip_prefix(line, "shallow ", &arg)) {
+ if (get_sha1_hex(arg, sha1))
die("invalid shallow line: %s", line);
register_shallow(sha1);
continue;
}
- if (starts_with(line, "unshallow ")) {
- if (get_sha1_hex(line + 10, sha1))
+ if (skip_prefix(line, "unshallow ", &arg)) {
+ if (get_sha1_hex(arg, sha1))
die("invalid unshallow line: %s", line);
if (!lookup_object(sha1))
die("object not found: %s", line);
diff --git a/fsck.c b/fsck.c
index abed62bac7..56156fff44 100644
--- a/fsck.c
+++ b/fsck.c
@@ -276,56 +276,42 @@ 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, *tmp;
unsigned char tree_sha1[20], sha1[20];
struct commit_graft *graft;
- int parents = 0;
+ unsigned parent_count, parent_line_count = 0;
int err;
- buffer = skip_prefix(buffer, "tree ");
- if (!buffer)
+ if (!skip_prefix(buffer, "tree ", &buffer))
return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n')
return error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1");
buffer += 41;
- while ((tmp = skip_prefix(buffer, "parent "))) {
- buffer = tmp;
+ while (skip_prefix(buffer, "parent ", &buffer)) {
if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n')
return error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1");
buffer += 41;
- parents++;
+ parent_line_count++;
}
graft = lookup_commit_graft(commit->object.sha1);
+ parent_count = commit_list_count(commit->parents);
if (graft) {
- struct commit_list *p = commit->parents;
- parents = 0;
- while (p) {
- p = p->next;
- parents++;
- }
- if (graft->nr_parent == -1 && !parents)
+ if (graft->nr_parent == -1 && !parent_count)
; /* shallow commit */
- else if (graft->nr_parent != parents)
+ else if (graft->nr_parent != parent_count)
return error_func(&commit->object, FSCK_ERROR, "graft objects missing");
} else {
- struct commit_list *p = commit->parents;
- while (p && parents) {
- p = p->next;
- parents--;
- }
- if (p || parents)
+ if (parent_count != parent_line_count)
return error_func(&commit->object, FSCK_ERROR, "parent objects missing");
}
- buffer = skip_prefix(buffer, "author ");
- if (!buffer)
+ if (!skip_prefix(buffer, "author ", &buffer))
return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line");
err = fsck_ident(&buffer, &commit->object, error_func);
if (err)
return err;
- buffer = skip_prefix(buffer, "committer ");
- if (!buffer)
+ if (!skip_prefix(buffer, "committer ", &buffer))
return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'committer' line");
err = fsck_ident(&buffer, &commit->object, error_func);
if (err)
@@ -336,6 +322,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;
diff --git a/git-bisect.sh b/git-bisect.sh
index af4d04c3be..1e0d602f4b 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -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"
diff --git a/git-compat-util.h b/git-compat-util.h
index b6f03b36dc..26e92f19cf 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -291,10 +291,12 @@ extern char *gitbasename(char *);
#else
#define NORETURN
#define NORETURN_PTR
+#ifndef __GNUC__
#ifndef __attribute__
#define __attribute__(x)
#endif
#endif
+#endif
/* The sentinel attribute is valid from gcc version 4.0 */
#if defined(__GNUC__) && (__GNUC__ >= 4)
@@ -347,15 +349,66 @@ 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);
-static inline const char *skip_prefix(const char *str, const char *prefix)
+/*
+ * If the string "str" begins with the string found in "prefix", return 1.
+ * The "out" parameter is set to "str + strlen(prefix)" (i.e., to the point in
+ * the string right after the prefix).
+ *
+ * Otherwise, return 0 and leave "out" untouched.
+ *
+ * Examples:
+ *
+ * [extract branch name, fail if not a branch]
+ * if (!skip_prefix(ref, "refs/heads/", &branch)
+ * return -1;
+ *
+ * [skip prefix if present, otherwise use whole string]
+ * skip_prefix(name, "refs/heads/", &name);
+ */
+static inline int skip_prefix(const char *str, const char *prefix,
+ const char **out)
{
do {
- if (!*prefix)
- return str;
+ if (!*prefix) {
+ *out = str;
+ return 1;
+ }
} while (*str++ == *prefix++);
- return NULL;
+ 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)
@@ -562,13 +615,6 @@ static inline size_t xsize_t(off_t len)
return (size_t)len;
}
-static inline int has_extension(const char *filename, const char *ext)
-{
- size_t len = strlen(filename);
- size_t extlen = strlen(ext);
- return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
-}
-
/* in ctype.c, for kwset users */
extern const char tolower_trans_tbl[256];
@@ -685,6 +731,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
@@ -704,6 +761,10 @@ void git_qsort(void *base, size_t nmemb, size_t size,
#endif
#endif
+#if defined(__GNUC__) || (_MSC_VER >= 1400)
+#define HAVE_VARIADIC_MACROS 1
+#endif
+
/*
* Preserves errno, prints a message, but gives no warning for ENOENT.
* Always returns the return value of unlink(2).
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 86d6994619..e6e99f5bb5 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -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
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 6a8907e7b3..b186329d28 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -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
diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl
index 30d9a79776..b0a5180af7 100644
--- a/git-gui/lib/diff.tcl
+++ b/git-gui/lib/diff.tcl
@@ -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
diff --git a/git-mergetool.sh b/git-mergetool.sh
index d08dc92589..9a046b75d1 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -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"
diff --git a/git-p4.py b/git-p4.py
index 7bb0f7313b..ff132b2117 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -1238,7 +1238,7 @@ class P4Submit(Command, P4UserMap):
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 @@ class P4Submit(Command, P4UserMap):
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 @@ class P4Submit(Command, P4UserMap):
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 @@ class P4Submit(Command, P4UserMap):
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:
diff --git a/git-rebase--am.sh b/git-rebase--am.sh
index ca20e1e66f..f923732333 100644
--- a/git-rebase--am.sh
+++ b/git-rebase--am.sh
@@ -29,7 +29,13 @@ skip)
;;
esac
-test -n "$rebase_root" && root_flag=--root
+if test -z "$rebase_root"
+ # this is now equivalent to ! -z "$upstream"
+then
+ revisions=$upstream...$orig_head
+else
+ revisions=$onto...$orig_head
+fi
ret=0
if test -n "$keep_empty"
@@ -38,14 +44,17 @@ then
# empty commits and even if it didn't the format doesn't really lend
# itself well to recording empty patches. fortunately, cherry-pick
# makes this easy
- git cherry-pick ${gpg_sign_opt:+"$gpg_sign_opt"} --allow-empty "$revisions"
+ git cherry-pick ${gpg_sign_opt:+"$gpg_sign_opt"} --allow-empty \
+ --right-only "$revisions" \
+ ${restrict_revision+^$restrict_revision}
ret=$?
else
rm -f "$GIT_DIR/rebased-patches"
- git format-patch -k --stdout --full-index --ignore-if-in-upstream \
+ git format-patch -k --stdout --full-index --cherry-pick --right-only \
--src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
- $root_flag "$revisions" >"$GIT_DIR/rebased-patches"
+ "$revisions" ${restrict_revision+^$restrict_revision} \
+ >"$GIT_DIR/rebased-patches"
ret=$?
if test 0 != $ret
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index f267d8b6c3..b64dd28acf 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -963,7 +963,7 @@ else
fi
git rev-list $merges_option --pretty=oneline --abbrev-commit \
--abbrev=7 --reverse --left-right --topo-order \
- $revisions | \
+ $revisions ${restrict_revision+^$restrict_revision} | \
sed -n "s/^>//p" |
while read -r shortsha1 rest
do
@@ -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,
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index 6d77b3ca91..d3fb67d75b 100644
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -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
diff --git a/git-rebase.sh b/git-rebase.sh
index 06c810b64f..55da9db818 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -59,6 +59,7 @@ If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".')
"
unset onto
+unset restrict_revision
cmd=
strategy=
strategy_opts=
@@ -546,7 +547,7 @@ then
"${switch_to:-HEAD}")
if test -n "$new_upstream"
then
- upstream=$new_upstream
+ restrict_revision=$new_upstream
fi
fi
@@ -572,7 +573,7 @@ require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
# and if this is not an interactive rebase.
mb=$(git merge-base "$onto" "$orig_head")
if test "$type" != interactive && test "$upstream" = "$onto" &&
- test "$mb" = "$onto" &&
+ test "$mb" = "$onto" && test -z "$restrict_revision" &&
# linear history?
! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
then
@@ -626,7 +627,7 @@ if test -n "$rebase_root"
then
revisions="$onto..$orig_head"
else
- revisions="$upstream..$orig_head"
+ revisions="${restrict_revision-$upstream}..$orig_head"
fi
run_specific_rebase
diff --git a/git-submodule.sh b/git-submodule.sh
index e146b833d1..9245abfd42 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -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 7780572948..9c49519831 100644
--- 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) {
@@ -54,8 +91,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
/*
* Check remaining flags.
*/
- if (starts_with(cmd, "--exec-path")) {
- cmd += 11;
+ if (skip_prefix(cmd, "--exec-path", &cmd)) {
if (*cmd == '=')
git_set_argv_exec_path(cmd + 1);
else {
@@ -92,8 +128,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
*envchanged = 1;
(*argv)++;
(*argc)--;
- } else if (starts_with(cmd, "--git-dir=")) {
- setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
+ } else if (skip_prefix(cmd, "--git-dir=", &cmd)) {
+ setenv(GIT_DIR_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--namespace")) {
@@ -106,8 +142,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
*envchanged = 1;
(*argv)++;
(*argc)--;
- } else if (starts_with(cmd, "--namespace=")) {
- setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
+ } else if (skip_prefix(cmd, "--namespace=", &cmd)) {
+ setenv(GIT_NAMESPACE_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--work-tree")) {
@@ -120,8 +156,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
*envchanged = 1;
(*argv)++;
(*argc)--;
- } else if (starts_with(cmd, "--work-tree=")) {
- setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
+ } else if (skip_prefix(cmd, "--work-tree=", &cmd)) {
+ setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--bare")) {
@@ -272,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;
@@ -352,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 },
@@ -378,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 },
@@ -441,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 },
@@ -484,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));
}
}
@@ -539,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;
}
@@ -568,6 +613,8 @@ int main(int argc, char **av)
git_setup_gettext();
+ trace_command_performance(argv);
+
/*
* "git-xxxx" is the same as "git xxxx", but we obviously:
*
@@ -578,8 +625,7 @@ int main(int argc, char **av)
* So we just directly call the builtin handler, and die if
* that one cannot handle it.
*/
- if (starts_with(cmd, "git-")) {
- cmd += 4;
+ if (skip_prefix(cmd, "git-", &cmd)) {
argv[0] = cmd;
handle_builtin(argc, argv);
die("cannot handle %s as a builtin", cmd);
@@ -590,8 +636,8 @@ int main(int argc, char **av)
argc--;
handle_options(&argv, &argc, NULL);
if (argc > 0) {
- if (starts_with(argv[0], "--"))
- argv[0] += 2;
+ /* translate --help and --version into commands */
+ skip_prefix(argv[0], "--", &argv[0]);
} else {
/* The user didn't specify a command; give them help */
commit_pager_choice();
diff --git a/gitk-git/gitk b/gitk-git/gitk
index 90764e8948..c8df35dee5 100755
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -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
index 0000000000..4dfe125a69
--- /dev/null
+++ b/gitk-git/po/vi.po
@@ -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"
diff --git a/gpg-interface.c b/gpg-interface.c
index 8b0e87436b..ff07012726 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -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);
diff --git a/gpg-interface.h b/gpg-interface.h
index a85cb5bc97..37c23daff0 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -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/hashmap.c b/hashmap.c
index d1b8056d8d..f693839cb4 100644
--- a/hashmap.c
+++ b/hashmap.c
@@ -226,3 +226,41 @@ void *hashmap_iter_next(struct hashmap_iter *iter)
current = iter->map->table[iter->tablepos++];
}
}
+
+struct pool_entry {
+ struct hashmap_entry ent;
+ size_t len;
+ unsigned char data[FLEX_ARRAY];
+};
+
+static int pool_entry_cmp(const struct pool_entry *e1,
+ const struct pool_entry *e2,
+ const unsigned char *keydata)
+{
+ return e1->data != keydata &&
+ (e1->len != e2->len || memcmp(e1->data, keydata, e1->len));
+}
+
+const void *memintern(const void *data, size_t len)
+{
+ static struct hashmap map;
+ struct pool_entry key, *e;
+
+ /* initialize string pool hashmap */
+ if (!map.tablesize)
+ hashmap_init(&map, (hashmap_cmp_fn) pool_entry_cmp, 0);
+
+ /* lookup interned string in pool */
+ hashmap_entry_init(&key, memhash(data, len));
+ key.len = len;
+ e = hashmap_get(&map, &key, data);
+ if (!e) {
+ /* not found: create it */
+ e = xmallocz(sizeof(struct pool_entry) + len);
+ hashmap_entry_init(e, key.ent.hash);
+ e->len = len;
+ memcpy(e->data, data, len);
+ hashmap_add(&map, e);
+ }
+ return e->data;
+}
diff --git a/hashmap.h b/hashmap.h
index a816ad47b1..ab7958ae33 100644
--- a/hashmap.h
+++ b/hashmap.h
@@ -13,6 +13,17 @@ extern unsigned int strihash(const char *buf);
extern unsigned int memhash(const void *buf, size_t len);
extern unsigned int memihash(const void *buf, size_t len);
+static inline unsigned int sha1hash(const unsigned char *sha1)
+{
+ /*
+ * Equivalent to 'return *(unsigned int *)sha1;', but safe on
+ * platforms that don't support unaligned reads.
+ */
+ unsigned int hash;
+ memcpy(&hash, sha1, sizeof(hash));
+ return hash;
+}
+
/* data structures */
struct hashmap_entry {
@@ -57,6 +68,14 @@ extern void *hashmap_put(struct hashmap *map, void *entry);
extern void *hashmap_remove(struct hashmap *map, const void *key,
const void *keydata);
+static inline void *hashmap_get_from_hash(const struct hashmap *map,
+ unsigned int hash, const void *keydata)
+{
+ struct hashmap_entry key;
+ hashmap_entry_init(&key, hash);
+ return hashmap_get(map, &key, keydata);
+}
+
/* hashmap_iter functions */
extern void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);
@@ -68,4 +87,12 @@ static inline void *hashmap_iter_first(struct hashmap *map,
return hashmap_iter_next(iter);
}
+/* string interning */
+
+extern const void *memintern(const void *data, size_t len);
+static inline const char *strintern(const char *string)
+{
+ return memintern(string, strlen(string));
+}
+
#endif
diff --git a/help.c b/help.c
index b266b09320..7af65e205e 100644
--- a/help.c
+++ b/help.c
@@ -129,7 +129,6 @@ static void list_commands_in_dir(struct cmdnames *cmds,
const char *path,
const char *prefix)
{
- int prefix_len;
DIR *dir = opendir(path);
struct dirent *de;
struct strbuf buf = STRBUF_INIT;
@@ -139,15 +138,15 @@ static void list_commands_in_dir(struct cmdnames *cmds,
return;
if (!prefix)
prefix = "git-";
- prefix_len = strlen(prefix);
strbuf_addf(&buf, "%s/", path);
len = buf.len;
while ((de = readdir(dir)) != NULL) {
- int entlen;
+ const char *ent;
+ size_t entlen;
- if (!starts_with(de->d_name, prefix))
+ if (!skip_prefix(de->d_name, prefix, &ent))
continue;
strbuf_setlen(&buf, len);
@@ -155,11 +154,10 @@ static void list_commands_in_dir(struct cmdnames *cmds,
if (!is_executable(buf.buf))
continue;
- entlen = strlen(de->d_name) - prefix_len;
- if (has_extension(de->d_name, ".exe"))
- entlen -= 4;
+ entlen = strlen(ent);
+ strip_suffix(ent, ".exe", &entlen);
- add_cmdname(cmds, de->d_name + prefix_len, entlen);
+ add_cmdname(cmds, ent, entlen);
}
closedir(dir);
strbuf_release(&buf);
@@ -251,11 +249,13 @@ static struct cmdnames aliases;
static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
{
+ const char *p;
+
if (!strcmp(var, "help.autocorrect"))
autocorrect = git_config_int(var,value);
/* Also use aliases for command lookup */
- if (starts_with(var, "alias."))
- add_cmdname(&aliases, var + 6, strlen(var + 6));
+ if (skip_prefix(var, "alias.", &p))
+ add_cmdname(&aliases, p, strlen(p));
return git_default_config(var, value, cb);
}
@@ -412,11 +412,12 @@ static int append_similar_ref(const char *refname, const unsigned char *sha1,
{
struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
char *branch = strrchr(refname, '/') + 1;
+ const char *remote;
+
/* A remote branch of the same name is deemed similar */
- if (starts_with(refname, "refs/remotes/") &&
+ if (skip_prefix(refname, "refs/remotes/", &remote) &&
!strcmp(branch, cb->base_ref))
- string_list_append(cb->similar_refs,
- refname + strlen("refs/remotes/"));
+ string_list_append(cb->similar_refs, remote);
return 0;
}
diff --git a/http-backend.c b/http-backend.c
index d2c0a625ce..80790bbaef 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -221,17 +221,19 @@ static void get_idx_file(char *name)
static int http_config(const char *var, const char *value, void *cb)
{
+ const char *p;
+
if (!strcmp(var, "http.getanyfile")) {
getanyfile = git_config_bool(var, value);
return 0;
}
- if (starts_with(var, "http.")) {
+ if (skip_prefix(var, "http.", &p)) {
int i;
for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
struct rpc_service *svc = &rpc_service[i];
- if (!strcmp(var + 5, svc->config_name)) {
+ if (!strcmp(p, svc->config_name)) {
svc->enabled = git_config_bool(var, value);
return 0;
}
@@ -244,15 +246,16 @@ static int http_config(const char *var, const char *value, void *cb)
static struct rpc_service *select_service(const char *name)
{
+ const char *svc_name;
struct rpc_service *svc = NULL;
int i;
- if (!starts_with(name, "git-"))
+ if (!skip_prefix(name, "git-", &svc_name))
forbidden("Unsupported service: '%s'", name);
for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
struct rpc_service *s = &rpc_service[i];
- if (!strcmp(s->name, name + 4)) {
+ if (!strcmp(s->name, svc_name)) {
svc = s;
break;
}
@@ -607,9 +610,7 @@ int main(int argc, char **argv)
cmd = c;
n = out[0].rm_eo - out[0].rm_so;
- cmd_arg = xmalloc(n);
- memcpy(cmd_arg, dir + out[0].rm_so + 1, n-1);
- cmd_arg[n-1] = '\0';
+ cmd_arg = xmemdupz(dir + out[0].rm_so + 1, n - 1);
dir[out[0].rm_so] = 0;
break;
}
diff --git a/http-push.c b/http-push.c
index de00d1693a..952f8ede49 100644
--- a/http-push.c
+++ b/http-push.c
@@ -199,7 +199,7 @@ static void curl_setup_http(CURL *curl, const char *url,
curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
#ifndef NO_CURL_IOCTL
curl_easy_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
- curl_easy_setopt(curl, CURLOPT_IOCTLDATA, &buffer);
+ curl_easy_setopt(curl, CURLOPT_IOCTLDATA, buffer);
#endif
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_fn);
curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
@@ -719,14 +719,10 @@ static int fetch_indices(void)
return ret;
}
-static void one_remote_object(const char *hex)
+static void one_remote_object(const unsigned char *sha1)
{
- unsigned char sha1[20];
struct object *obj;
- if (get_sha1_hex(hex, sha1) != 0)
- return;
-
obj = lookup_object(sha1);
if (!obj)
obj = parse_object(sha1);
@@ -767,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)) {
- if (starts_with(ctx->cdata, "Second-"))
- lock->timeout =
- strtol(ctx->cdata + 7, NULL, 10);
+ 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));
@@ -856,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, '/');
@@ -1020,26 +1013,38 @@ static void remote_ls(const char *path, int flags,
void (*userFunc)(struct remote_ls_ctx *ls),
void *userData);
+/* extract hex from sharded "xx/x{40}" filename */
+static int get_sha1_hex_from_objpath(const char *path, unsigned char *sha1)
+{
+ char hex[40];
+
+ if (strlen(path) != 41)
+ return -1;
+
+ memcpy(hex, path, 2);
+ path += 2;
+ path++; /* skip '/' */
+ memcpy(hex, path, 38);
+
+ return get_sha1_hex(hex, sha1);
+}
+
static void process_ls_object(struct remote_ls_ctx *ls)
{
unsigned int *parent = (unsigned int *)ls->userData;
- char *path = ls->dentry_name;
- char *obj_hex;
+ const char *path = ls->dentry_name;
+ unsigned char sha1[20];
if (!strcmp(ls->path, ls->dentry_name) && (ls->flags & IS_DIR)) {
remote_dir_exists[*parent] = 1;
return;
}
- if (strlen(path) != 49)
+ if (!skip_prefix(path, "objects/", &path) ||
+ get_sha1_hex_from_objpath(path, sha1))
return;
- path += 8;
- obj_hex = xmalloc(strlen(path));
- /* NB: path is not null-terminated, can not use strlcpy here */
- memcpy(obj_hex, path, 2);
- strcpy(obj_hex + 2, path + 3);
- one_remote_object(obj_hex);
- free(obj_hex);
+
+ one_remote_object(sha1);
}
static void process_ls_ref(struct remote_ls_ctx *ls)
@@ -1117,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;
@@ -1133,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");
@@ -1536,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:
@@ -1559,11 +1561,9 @@ 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;
-
- url = xmalloc(strlen(repo->url) + strlen(path) + 1);
- sprintf(url, "%s%s", repo->url, path);
+ const char *name;
if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK)
die("Couldn't get %s for remote symref\n%s", url,
@@ -1578,8 +1578,8 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
return;
/* If it's a symref, set the refname; otherwise try for a sha1 */
- if (starts_with((char *)buffer.buf, "ref: ")) {
- *symref = xmemdupz((char *)buffer.buf + 5, buffer.len - 6);
+ if (skip_prefix(buffer.buf, "ref: ", &name)) {
+ *symref = xmemdupz(name, buffer.len - (name - buffer.buf));
} else {
get_sha1_hex(buffer.buf, sha1);
}
@@ -1673,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);
diff --git a/http-walker.c b/http-walker.c
index 1516c5eb29..dbddfaa177 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -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 2b4f6a357c..c8cd50dd0c 100644
--- 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;
@@ -1087,11 +1087,10 @@ static int update_url_from_redirect(struct strbuf *base,
if (!strcmp(asked, got->buf))
return 0;
- if (!starts_with(asked, base->buf))
+ if (!skip_prefix(asked, base->buf, &tail))
die("BUG: update_url_from_redirect: %s is not a superset of %s",
asked, base->buf);
- tail = asked + base->len;
tail_len = strlen(tail);
if (got->len < tail_len ||
diff --git a/imap-send.c b/imap-send.c
index 83a6ed2ac3..524fbabc96 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1328,13 +1328,9 @@ static char *imap_folder;
static int git_imap_config(const char *key, const char *val, void *cb)
{
- char imap_key[] = "imap.";
-
- if (strncmp(key, imap_key, sizeof imap_key - 1))
+ if (!skip_prefix(key, "imap.", &key))
return 0;
- key += sizeof imap_key - 1;
-
/* check booleans first, and barf on others */
if (!strcmp("sslverify", key))
server.ssl_verify = git_config_bool(key, val);
diff --git a/khash.h b/khash.h
index 57ff6038c5..06c7906549 100644
--- a/khash.h
+++ b/khash.h
@@ -320,19 +320,12 @@ static const double __ac_HASH_UPPER = 0.77;
code; \
} }
-static inline khint_t __kh_oid_hash(const unsigned char *oid)
-{
- khint_t hash;
- memcpy(&hash, oid, sizeof(hash));
- return hash;
-}
-
#define __kh_oid_cmp(a, b) (hashcmp(a, b) == 0)
-KHASH_INIT(sha1, const unsigned char *, void *, 1, __kh_oid_hash, __kh_oid_cmp)
+KHASH_INIT(sha1, const unsigned char *, void *, 1, sha1hash, __kh_oid_cmp)
typedef kh_sha1_t khash_sha1;
-KHASH_INIT(sha1_pos, const unsigned char *, int, 1, __kh_oid_hash, __kh_oid_cmp)
+KHASH_INIT(sha1_pos, const unsigned char *, int, 1, sha1hash, __kh_oid_cmp)
typedef kh_sha1_pos_t khash_sha1_pos;
#endif /* __AC_KHASH_H */
diff --git a/line-log.c b/line-log.c
index 1500101058..1008e72258 100644
--- a/line-log.c
+++ b/line-log.c
@@ -766,17 +766,6 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
}
}
-static int count_parents(struct commit *commit)
-{
- struct commit_list *parents = commit->parents;
- int count = 0;
- while (parents) {
- count++;
- parents = parents->next;
- }
- return count;
-}
-
static void move_diff_queue(struct diff_queue_struct *dst,
struct diff_queue_struct *src)
{
@@ -1150,7 +1139,7 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
struct commit **parents;
struct commit_list *p;
int i;
- int nparents = count_parents(commit);
+ int nparents = commit_list_count(commit->parents);
diffqueues = xmalloc(nparents * sizeof(*diffqueues));
cand = xmalloc(nparents * sizeof(*cand));
@@ -1174,9 +1163,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);
diff --git a/lockfile.c b/lockfile.c
index 8fbcb6a98a..2564a7f544 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -5,7 +5,6 @@
#include "sigchain.h"
static struct lock_file *lock_file_list;
-static const char *alternate_index_output;
static void remove_lock_file(void)
{
@@ -121,7 +120,7 @@ static char *resolve_symlink(char *p, size_t s)
return p;
}
-
+/* Make sure errno contains a meaningful value on error */
static int lock_file(struct lock_file *lk, const char *path, int flags)
{
/*
@@ -130,8 +129,10 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
*/
static const size_t max_path_len = sizeof(lk->filename) - 5;
- if (strlen(path) >= max_path_len)
+ if (strlen(path) >= max_path_len) {
+ errno = ENAMETOOLONG;
return -1;
+ }
strcpy(lk->filename, path);
if (!(flags & LOCK_NODEREF))
resolve_symlink(lk->filename, max_path_len);
@@ -148,44 +149,51 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
lock_file_list = lk;
lk->on_list = 1;
}
- if (adjust_shared_perm(lk->filename))
- return error("cannot fix permission bits on %s",
- lk->filename);
+ if (adjust_shared_perm(lk->filename)) {
+ int save_errno = errno;
+ error("cannot fix permission bits on %s",
+ lk->filename);
+ errno = save_errno;
+ return -1;
+ }
}
else
lk->filename[0] = 0;
return lk->fd;
}
-static char *unable_to_lock_message(const char *path, int err)
+void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
{
- struct strbuf buf = STRBUF_INIT;
-
if (err == EEXIST) {
- strbuf_addf(&buf, "Unable to create '%s.lock': %s.\n\n"
+ strbuf_addf(buf, "Unable to create '%s.lock': %s.\n\n"
"If no other git process is currently running, this probably means a\n"
"git process crashed in this repository earlier. Make sure no other git\n"
"process is running and remove the file manually to continue.",
absolute_path(path), strerror(err));
} else
- strbuf_addf(&buf, "Unable to create '%s.lock': %s",
+ strbuf_addf(buf, "Unable to create '%s.lock': %s",
absolute_path(path), strerror(err));
- return strbuf_detach(&buf, NULL);
}
int unable_to_lock_error(const char *path, int err)
{
- char *msg = unable_to_lock_message(path, err);
- error("%s", msg);
- free(msg);
+ struct strbuf buf = STRBUF_INIT;
+
+ unable_to_lock_message(path, err, &buf);
+ error("%s", buf.buf);
+ strbuf_release(&buf);
return -1;
}
NORETURN void unable_to_lock_index_die(const char *path, int err)
{
- die("%s", unable_to_lock_message(path, err));
+ struct strbuf buf = STRBUF_INIT;
+
+ unable_to_lock_message(path, err, &buf);
+ die("%s", buf.buf);
}
+/* This should return a meaningful errno on failure */
int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
{
int fd = lock_file(lk, path, flags);
@@ -252,25 +260,6 @@ int hold_locked_index(struct lock_file *lk, int die_on_error)
: 0);
}
-void set_alternate_index_output(const char *name)
-{
- alternate_index_output = name;
-}
-
-int commit_locked_index(struct lock_file *lk)
-{
- if (alternate_index_output) {
- if (lk->fd >= 0 && close_lock_file(lk))
- return -1;
- if (rename(lk->filename, alternate_index_output))
- return -1;
- lk->filename[0] = 0;
- return 0;
- }
- else
- return commit_lock_file(lk);
-}
-
void rollback_lock_file(struct lock_file *lk)
{
if (lk->filename[0]) {
diff --git a/log-tree.c b/log-tree.c
index cf2f86c866..0c53dc11ab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -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,
@@ -413,10 +414,11 @@ static int is_common_merge(const struct commit *commit)
&& !commit->parents->next->next);
}
-static void show_one_mergetag(struct rev_info *opt,
+static void show_one_mergetag(struct commit *commit,
struct commit_extra_header *extra,
- struct commit *commit)
+ void *data)
{
+ struct rev_info *opt = (struct rev_info *)data;
unsigned char sha1[20];
struct tag *tag;
struct strbuf verify_message;
@@ -446,16 +448,17 @@ static void show_one_mergetag(struct rev_info *opt,
payload_size = parse_signature(extra->value, extra->len);
status = -1;
- if (extra->len > payload_size)
- if (verify_signed_buffer(extra->value, payload_size,
- extra->value + payload_size,
- extra->len - payload_size,
- &verify_message, NULL)) {
- if (verify_message.len <= gpg_message_offset)
- strbuf_addstr(&verify_message, "No signature\n");
- else
- status = 0;
- }
+ if (extra->len > payload_size) {
+ /* could have a good signature */
+ if (!verify_signed_buffer(extra->value, payload_size,
+ extra->value + payload_size,
+ extra->len - payload_size,
+ &verify_message, NULL))
+ status = 0; /* good */
+ else if (verify_message.len <= gpg_message_offset)
+ strbuf_addstr(&verify_message, "No signature\n");
+ /* otherwise we couldn't verify, which is shown as bad */
+ }
show_sig_lines(opt, status, verify_message.buf);
strbuf_release(&verify_message);
@@ -463,15 +466,7 @@ static void show_one_mergetag(struct rev_info *opt,
static void show_mergetag(struct rev_info *opt, struct commit *commit)
{
- struct commit_extra_header *extra, *to_free;
-
- to_free = read_commit_extra_headers(commit, NULL);
- for (extra = to_free; extra; extra = extra->next) {
- if (strcmp(extra->key, "mergetag"))
- continue; /* not a merge tag */
- show_one_mergetag(opt, extra, commit);
- }
- free_commit_extra_headers(to_free);
+ for_each_mergetag(show_one_mergetag, commit, opt);
}
void show_log(struct rev_info *opt)
@@ -588,7 +583,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) {
diff --git a/match-trees.c b/match-trees.c
index e80b4af354..1ce0954a3e 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -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);
diff --git a/merge-recursive.c b/merge-recursive.c
index cab16fafb5..1d332b8bbb 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -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;
@@ -171,7 +171,7 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)
strbuf_vaddf(&o->obuf, fmt, ap);
va_end(ap);
- strbuf_add(&o->obuf, "\n", 1);
+ strbuf_addch(&o->obuf, '\n');
if (!o->buffer_output)
flush_output(o);
}
@@ -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;
@@ -2053,16 +2059,15 @@ void init_merge_options(struct merge_options *o)
if (o->verbosity >= 5)
o->buffer_output = 0;
strbuf_init(&o->obuf, 0);
- memset(&o->current_file_set, 0, sizeof(struct string_list));
- o->current_file_set.strdup_strings = 1;
- memset(&o->current_directory_set, 0, sizeof(struct string_list));
- o->current_directory_set.strdup_strings = 1;
- memset(&o->df_conflict_file_set, 0, sizeof(struct string_list));
- o->df_conflict_file_set.strdup_strings = 1;
+ string_list_init(&o->current_file_set, 1);
+ string_list_init(&o->current_directory_set, 1);
+ string_list_init(&o->df_conflict_file_set, 1);
}
int parse_merge_opt(struct merge_options *o, const char *s)
{
+ const char *arg;
+
if (!s || !*s)
return -1;
if (!strcmp(s, "ours"))
@@ -2071,14 +2076,14 @@ int parse_merge_opt(struct merge_options *o, const char *s)
o->recursive_variant = MERGE_RECURSIVE_THEIRS;
else if (!strcmp(s, "subtree"))
o->subtree_shift = "";
- else if (starts_with(s, "subtree="))
- o->subtree_shift = s + strlen("subtree=");
+ else if (skip_prefix(s, "subtree=", &arg))
+ o->subtree_shift = arg;
else if (!strcmp(s, "patience"))
o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
else if (!strcmp(s, "histogram"))
o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
- else if (starts_with(s, "diff-algorithm=")) {
- long value = parse_algorithm_value(s + strlen("diff-algorithm="));
+ else if (skip_prefix(s, "diff-algorithm=", &arg)) {
+ long value = parse_algorithm_value(arg);
if (value < 0)
return -1;
/* clear out previous settings */
@@ -2096,9 +2101,8 @@ int parse_merge_opt(struct merge_options *o, const char *s)
o->renormalize = 1;
else if (!strcmp(s, "no-renormalize"))
o->renormalize = 0;
- else if (starts_with(s, "rename-threshold=")) {
- const char *score = s + strlen("rename-threshold=");
- if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0)
+ else if (skip_prefix(s, "rename-threshold=", &arg)) {
+ if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
return -1;
}
else
diff --git a/merge.c b/merge.c
index 70f1000fcb..74ced7f70b 100644
--- 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;
}
diff --git a/name-hash.c b/name-hash.c
index 97444d0201..702cd0518f 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -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)
@@ -213,12 +213,11 @@ struct cache_entry *index_dir_exists(struct index_state *istate, const char *nam
struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int icase)
{
struct cache_entry *ce;
- struct hashmap_entry key;
lazy_init_name_hash(istate);
- hashmap_entry_init(&key, memihash(name, namelen));
- ce = hashmap_get(&istate->name_hash, &key, NULL);
+ ce = hashmap_get_from_hash(&istate->name_hash,
+ memihash(name, namelen), NULL);
while (ce) {
if (same_name(ce, name, namelen, icase))
return ce;
diff --git a/notes-cache.c b/notes-cache.c
index 97dfd63c9b..c4e9bb7f6c 100644
--- a/notes-cache.c
+++ b/notes-cache.c
@@ -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)
diff --git a/notes-merge.c b/notes-merge.c
index 94a1a8ae46..fd5fae255d 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -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));
diff --git a/notes-utils.c b/notes-utils.c
index a0b1d7be98..b64dc1b021 100644
--- a/notes-utils.c
+++ b/notes-utils.c
@@ -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);
diff --git a/notes-utils.h b/notes-utils.h
index 564e30cccd..890ddb33e1 100644
--- a/notes-utils.h
+++ b/notes-utils.h
@@ -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);
diff --git a/object.c b/object.c
index 57a0890a87..a16b9f9e93 100644
--- a/object.c
+++ b/object.c
@@ -50,18 +50,7 @@ int type_from_string(const char *str)
*/
static unsigned int hash_obj(const unsigned char *sha1, unsigned int n)
{
- unsigned int hash;
-
- /*
- * Since the sha1 is essentially random, we just take the
- * required number of bits directly from the first
- * sizeof(unsigned int) bytes of sha1. First we have to copy
- * the bytes into a properly aligned integer. If we cared
- * about getting consistent results across architectures, we
- * would have to call ntohl() here, too.
- */
- memcpy(&hash, sha1, sizeof(unsigned int));
- return hash & (n - 1);
+ return sha1hash(sha1) & (n - 1);
}
/*
@@ -141,13 +130,12 @@ static void grow_object_hash(void)
obj_hash_size = new_hash_size;
}
-void *create_object(const unsigned char *sha1, int type, void *o)
+void *create_object(const unsigned char *sha1, void *o)
{
struct object *obj = o;
obj->parsed = 0;
obj->used = 0;
- obj->type = type;
obj->flags = 0;
hashcpy(obj->sha1, sha1);
@@ -159,11 +147,30 @@ void *create_object(const unsigned char *sha1, int type, void *o)
return obj;
}
+void *object_as_type(struct object *obj, enum object_type type, int quiet)
+{
+ if (obj->type == type)
+ return obj;
+ else if (obj->type == OBJ_NONE) {
+ if (type == OBJ_COMMIT)
+ ((struct commit *)obj)->index = alloc_commit_index();
+ obj->type = type;
+ return obj;
+ }
+ else {
+ if (!quiet)
+ error("object %s is a %s, not a %s",
+ sha1_to_hex(obj->sha1),
+ typename(obj->type), typename(type));
+ return NULL;
+ }
+}
+
struct object *lookup_unknown_object(const unsigned char *sha1)
{
struct object *obj = lookup_object(sha1);
if (!obj)
- obj = create_object(sha1, OBJ_NONE, alloc_object_node());
+ obj = create_object(sha1, alloc_object_node());
return obj;
}
@@ -197,8 +204,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;
@@ -214,8 +221,6 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
warning("object %s has unknown type id %d", sha1_to_hex(sha1), type);
obj = NULL;
}
- if (obj && obj->type == OBJ_NONE)
- obj->type = type;
return obj;
}
diff --git a/object.h b/object.h
index 6e12f2c7f4..5e8d8ee548 100644
--- a/object.h
+++ b/object.h
@@ -79,7 +79,9 @@ extern struct object *get_indexed_object(unsigned int);
*/
struct object *lookup_object(const unsigned char *sha1);
-extern void *create_object(const unsigned char *sha1, int type, void *obj);
+extern void *create_object(const unsigned char *sha1, void *obj);
+
+void *object_as_type(struct object *obj, enum object_type type, int quiet);
/*
* Returns the object, having parsed it to find out what it is.
diff --git a/pack-objects.c b/pack-objects.c
index 4f36c32045..9992f3ecf2 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -7,10 +7,9 @@ static uint32_t locate_object_entry_hash(struct packing_data *pdata,
const unsigned char *sha1,
int *found)
{
- uint32_t i, hash, mask = (pdata->index_size - 1);
+ uint32_t i, mask = (pdata->index_size - 1);
- memcpy(&hash, sha1, sizeof(uint32_t));
- i = hash & mask;
+ i = sha1hash(sha1) & mask;
while (pdata->index[i] > 0) {
uint32_t pos = pdata->index[i] - 1;
diff --git a/parse-options.c b/parse-options.c
index b536896f26..e7dafa80d5 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -231,7 +231,8 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
continue;
again:
- rest = skip_prefix(arg, long_name);
+ if (!skip_prefix(arg, long_name, &rest))
+ rest = NULL;
if (options->type == OPTION_ARGUMENT) {
if (!rest)
continue;
@@ -280,12 +281,13 @@ is_abbreviated:
continue;
}
flags |= OPT_UNSET;
- rest = skip_prefix(arg + 3, long_name);
- /* abbreviated and negated? */
- if (!rest && starts_with(long_name, arg + 3))
- goto is_abbreviated;
- if (!rest)
- continue;
+ if (!skip_prefix(arg + 3, long_name, &rest)) {
+ /* abbreviated and negated? */
+ if (starts_with(long_name, arg + 3))
+ goto is_abbreviated;
+ else
+ continue;
+ }
}
if (*rest) {
if (*rest != '=')
diff --git a/path.c b/path.c
index bc804a31b3..3afcdb432a 100644
--- a/path.c
+++ b/path.c
@@ -249,9 +249,7 @@ int validate_headref(const char *path)
static struct passwd *getpw_str(const char *username, size_t len)
{
struct passwd *pw;
- char *username_z = xmalloc(len + 1);
- memcpy(username_z, username, len);
- username_z[len] = '\0';
+ char *username_z = xmemdupz(username, len);
pw = getpwnam(username_z);
free(username_z);
return pw;
@@ -277,16 +275,16 @@ char *expand_user_path(const char *path)
const char *home = getenv("HOME");
if (!home)
goto return_null;
- strbuf_add(&user_path, home, strlen(home));
+ strbuf_addstr(&user_path, home);
} else {
struct passwd *pw = getpw_str(username, username_len);
if (!pw)
goto return_null;
- strbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));
+ strbuf_addstr(&user_path, pw->pw_dir);
}
to_copy = first_slash;
}
- strbuf_add(&user_path, to_copy, strlen(to_copy));
+ strbuf_addstr(&user_path, to_copy);
return strbuf_detach(&user_path, NULL);
return_null:
strbuf_release(&user_path);
diff --git a/pathspec.c b/pathspec.c
index 8043099955..9304ee33d7 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -338,7 +338,7 @@ static void NORETURN unsupported_magic(const char *pattern,
if (!(magic & m->bit))
continue;
if (sb.len)
- strbuf_addstr(&sb, " ");
+ strbuf_addch(&sb, ' ');
if (short_magic & m->bit)
strbuf_addf(&sb, "'%c'", m->mnemonic);
else
@@ -389,8 +389,7 @@ void parse_pathspec(struct pathspec *pathspec,
if (!(flags & PATHSPEC_PREFER_CWD))
die("BUG: PATHSPEC_PREFER_CWD requires arguments");
- pathspec->items = item = xmalloc(sizeof(*item));
- memset(item, 0, sizeof(*item));
+ pathspec->items = item = xcalloc(1, sizeof(*item));
item->match = prefix;
item->original = prefix;
item->nowildcard_len = item->len = strlen(prefix);
diff --git a/pkt-line.c b/pkt-line.c
index bc63b3b80e..8bc89b1e0c 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -3,7 +3,7 @@
char packet_buffer[LARGE_PACKET_MAX];
static const char *packet_trace_prefix = "git";
-static const char trace_key[] = "GIT_TRACE_PACKET";
+static struct trace_key trace_packet = TRACE_KEY_INIT(PACKET);
void packet_trace_identity(const char *prog)
{
@@ -15,7 +15,7 @@ static void packet_trace(const char *buf, unsigned int len, int write)
int i;
struct strbuf out;
- if (!trace_want(trace_key))
+ if (!trace_want(&trace_packet))
return;
/* +32 is just a guess for header + quoting */
@@ -27,7 +27,7 @@ static void packet_trace(const char *buf, unsigned int len, int write)
if ((len >= 4 && starts_with(buf, "PACK")) ||
(len >= 5 && starts_with(buf+1, "PACK"))) {
strbuf_addstr(&out, "PACK ...");
- unsetenv(trace_key);
+ trace_disable(&trace_packet);
}
else {
/* XXX we should really handle printable utf8 */
@@ -43,7 +43,7 @@ static void packet_trace(const char *buf, unsigned int len, int write)
}
strbuf_addch(&out, '\n');
- trace_strbuf(trace_key, &out);
+ trace_strbuf(&trace_packet, &out);
strbuf_release(&out);
}
diff --git a/po/git.pot b/po/git.pot
index 1407f06e8d..1c8309a887 100644
--- a/po/git.pot
+++ b/po/git.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2014-04-19 12:50+0800\n"
+"POT-Creation-Date: 2014-08-04 14:48+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -23,12 +23,11 @@ msgstr ""
msgid "hint: %.*s\n"
msgstr ""
-#: advice.c:85
+#: advice.c:88
msgid ""
-"Fix them up in the work tree,\n"
-"and then use 'git add/rm <file>' as\n"
-"appropriate to mark resolution and make a commit,\n"
-"or use 'git commit -a'."
+"Fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution and make a commit, or use\n"
+"'git commit -a'."
msgstr ""
#: archive.c:10
@@ -48,7 +47,7 @@ msgstr ""
msgid "git archive --remote <repo> [--exec <cmd>] --list"
msgstr ""
-#: archive.c:243 builtin/add.c:136 builtin/add.c:428 builtin/rm.c:328
+#: archive.c:243 builtin/add.c:136 builtin/add.c:427 builtin/rm.c:328
#, c-format
msgid "pathspec '%s' did not match any files"
msgstr ""
@@ -61,7 +60,7 @@ msgstr ""
msgid "archive format"
msgstr ""
-#: archive.c:329 builtin/log.c:1193
+#: archive.c:329 builtin/log.c:1201
msgid "prefix"
msgstr ""
@@ -69,9 +68,9 @@ msgstr ""
msgid "prepend prefix to each pathname in the archive"
msgstr ""
-#: archive.c:331 builtin/archive.c:88 builtin/blame.c:2258
-#: builtin/blame.c:2259 builtin/config.c:57 builtin/fast-export.c:680
-#: builtin/fast-export.c:682 builtin/grep.c:714 builtin/hash-object.c:77
+#: archive.c:331 builtin/archive.c:88 builtin/blame.c:2517
+#: builtin/blame.c:2518 builtin/config.c:57 builtin/fast-export.c:709
+#: builtin/fast-export.c:711 builtin/grep.c:712 builtin/hash-object.c:77
#: builtin/ls-files.c:489 builtin/ls-files.c:492 builtin/notes.c:412
#: builtin/notes.c:569 builtin/read-tree.c:108 parse-options.h:151
msgid "file"
@@ -246,7 +245,7 @@ msgstr ""
msgid "unrecognized header: %s%s (%d)"
msgstr ""
-#: bundle.c:86 builtin/commit.c:706
+#: bundle.c:86 builtin/commit.c:755
#, c-format
msgid "could not open '%s'"
msgstr ""
@@ -255,8 +254,8 @@ msgstr ""
msgid "Repository lacks these prerequisite commits:"
msgstr ""
-#: bundle.c:162 sequencer.c:669 sequencer.c:1123 builtin/log.c:332
-#: builtin/log.c:821 builtin/log.c:1418 builtin/log.c:1644 builtin/merge.c:357
+#: bundle.c:162 sequencer.c:630 sequencer.c:1085 builtin/log.c:330
+#: builtin/log.c:821 builtin/log.c:1428 builtin/log.c:1665 builtin/merge.c:357
#: builtin/shortlog.c:158
msgid "revision walk setup failed"
msgstr ""
@@ -279,47 +278,47 @@ msgid_plural "The bundle requires these %d refs:"
msgstr[0] ""
msgstr[1] ""
-#: bundle.c:293
+#: bundle.c:289
msgid "rev-list died"
msgstr ""
-#: bundle.c:299 builtin/log.c:1329 builtin/shortlog.c:261
+#: bundle.c:295 builtin/log.c:1339 builtin/shortlog.c:261
#, c-format
msgid "unrecognized argument: %s"
msgstr ""
-#: bundle.c:334
+#: bundle.c:330
#, c-format
msgid "ref '%s' is excluded by the rev-list options"
msgstr ""
-#: bundle.c:379
+#: bundle.c:375
msgid "Refusing to create empty bundle."
msgstr ""
-#: bundle.c:395
+#: bundle.c:390
msgid "Could not spawn pack-objects"
msgstr ""
-#: bundle.c:413
+#: bundle.c:408
msgid "pack-objects died"
msgstr ""
-#: bundle.c:416
+#: bundle.c:411
#, c-format
msgid "cannot create '%s'"
msgstr ""
-#: bundle.c:438
+#: bundle.c:433
msgid "index-pack died"
msgstr ""
-#: commit.c:54
+#: commit.c:40
#, c-format
msgid "could not parse %s"
msgstr ""
-#: commit.c:56
+#: commit.c:42
#, c-format
msgid "%s %s is not a commit!"
msgstr ""
@@ -415,91 +414,100 @@ msgstr[1] ""
msgid "failed to read orderfile '%s'"
msgstr ""
-#: diffcore-rename.c:517
+#: diffcore-rename.c:514
msgid "Performing inexact rename detection"
msgstr ""
-#: diff.c:113
+#: diff.c:114
#, c-format
msgid " Failed to parse dirstat cut-off percentage '%s'\n"
msgstr ""
-#: diff.c:118
+#: diff.c:119
#, c-format
msgid " Unknown dirstat parameter '%s'\n"
msgstr ""
-#: diff.c:213
+#: diff.c:214
#, c-format
msgid "Unknown value for 'diff.submodule' config variable: '%s'"
msgstr ""
-#: diff.c:263
+#: diff.c:267
#, c-format
msgid ""
"Found errors in 'diff.dirstat' config variable:\n"
"%s"
msgstr ""
-#: diff.c:3495
+#: diff.c:2934
+#, c-format
+msgid "external diff died, stopping at %s"
+msgstr ""
+
+#: diff.c:3329
+msgid "--follow requires exactly one pathspec"
+msgstr ""
+
+#: diff.c:3492
#, c-format
msgid ""
"Failed to parse --dirstat/-X option parameter:\n"
"%s"
msgstr ""
-#: diff.c:3509
+#: diff.c:3506
#, c-format
msgid "Failed to parse --submodule option parameter: '%s'"
msgstr ""
-#: gpg-interface.c:59 gpg-interface.c:131
+#: gpg-interface.c:73 gpg-interface.c:145
msgid "could not run gpg."
msgstr ""
-#: gpg-interface.c:71
+#: gpg-interface.c:85
msgid "gpg did not accept the data"
msgstr ""
-#: gpg-interface.c:82
+#: gpg-interface.c:96
msgid "gpg failed to sign the data"
msgstr ""
-#: gpg-interface.c:115
+#: gpg-interface.c:129
#, c-format
msgid "could not create temporary file '%s': %s"
msgstr ""
-#: gpg-interface.c:118
+#: gpg-interface.c:132
#, c-format
msgid "failed writing detached signature to '%s': %s"
msgstr ""
-#: grep.c:1698
+#: grep.c:1703
#, c-format
msgid "'%s': unable to read %s"
msgstr ""
-#: grep.c:1715
+#: grep.c:1720
#, c-format
msgid "'%s': %s"
msgstr ""
-#: grep.c:1726
+#: grep.c:1731
#, c-format
msgid "'%s': short read %s"
msgstr ""
-#: help.c:209
+#: help.c:207
#, c-format
msgid "available git commands in '%s'"
msgstr ""
-#: help.c:216
+#: help.c:214
msgid "git commands available from elsewhere on your $PATH"
msgstr ""
-#: help.c:232
+#: help.c:230
msgid "The most commonly used git commands are:"
msgstr ""
@@ -531,7 +539,7 @@ msgstr ""
msgid "git: '%s' is not a git command. See 'git --help'."
msgstr ""
-#: help.c:384 help.c:443
+#: help.c:384 help.c:444
msgid ""
"\n"
"Did you mean this?"
@@ -541,16 +549,16 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: help.c:439
+#: help.c:440
#, c-format
msgid "%s: %s - %s"
msgstr ""
-#: merge.c:56
+#: merge.c:40
msgid "failed to read the cache"
msgstr ""
-#: merge.c:110 builtin/checkout.c:357 builtin/checkout.c:558
+#: merge.c:93 builtin/checkout.c:356 builtin/checkout.c:556
#: builtin/clone.c:661
msgid "unable to write new index file"
msgstr ""
@@ -560,7 +568,7 @@ msgstr ""
msgid "(bad commit)\n"
msgstr ""
-#: merge-recursive.c:208
+#: merge-recursive.c:210
#, c-format
msgid "addinfo_cache failed for path '%s'"
msgstr ""
@@ -569,284 +577,284 @@ msgstr ""
msgid "error building trees"
msgstr ""
-#: merge-recursive.c:675
+#: merge-recursive.c:692
#, c-format
msgid "failed to create path '%s'%s"
msgstr ""
-#: merge-recursive.c:686
+#: merge-recursive.c:703
#, c-format
msgid "Removing %s to make room for subdirectory\n"
msgstr ""
-#: merge-recursive.c:700 merge-recursive.c:721
+#: merge-recursive.c:717 merge-recursive.c:738
msgid ": perhaps a D/F conflict?"
msgstr ""
-#: merge-recursive.c:711
+#: merge-recursive.c:728
#, c-format
msgid "refusing to lose untracked file at '%s'"
msgstr ""
-#: merge-recursive.c:751
+#: merge-recursive.c:768
#, c-format
msgid "cannot read object %s '%s'"
msgstr ""
-#: merge-recursive.c:753
+#: merge-recursive.c:770
#, c-format
msgid "blob expected for %s '%s'"
msgstr ""
-#: merge-recursive.c:776 builtin/clone.c:317
+#: merge-recursive.c:793 builtin/clone.c:317
#, c-format
msgid "failed to open '%s'"
msgstr ""
-#: merge-recursive.c:784
+#: merge-recursive.c:801
#, c-format
msgid "failed to symlink '%s'"
msgstr ""
-#: merge-recursive.c:787
+#: merge-recursive.c:804
#, c-format
msgid "do not know what to do with %06o %s '%s'"
msgstr ""
-#: merge-recursive.c:925
+#: merge-recursive.c:942
msgid "Failed to execute internal merge"
msgstr ""
-#: merge-recursive.c:929
+#: merge-recursive.c:946
#, c-format
msgid "Unable to add %s to database"
msgstr ""
-#: merge-recursive.c:945
+#: merge-recursive.c:962
msgid "unsupported object type in the tree"
msgstr ""
-#: merge-recursive.c:1024 merge-recursive.c:1038
+#: merge-recursive.c:1037 merge-recursive.c:1051
#, c-format
msgid ""
"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
"in tree."
msgstr ""
-#: merge-recursive.c:1030 merge-recursive.c:1043
+#: merge-recursive.c:1043 merge-recursive.c:1056
#, c-format
msgid ""
"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
"in tree at %s."
msgstr ""
-#: merge-recursive.c:1084
+#: merge-recursive.c:1097
msgid "rename"
msgstr ""
-#: merge-recursive.c:1084
+#: merge-recursive.c:1097
msgid "renamed"
msgstr ""
-#: merge-recursive.c:1140
+#: merge-recursive.c:1153
#, c-format
msgid "%s is a directory in %s adding as %s instead"
msgstr ""
-#: merge-recursive.c:1162
+#: merge-recursive.c:1175
#, c-format
msgid ""
"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
"\"->\"%s\" in \"%s\"%s"
msgstr ""
-#: merge-recursive.c:1167
+#: merge-recursive.c:1180
msgid " (left unresolved)"
msgstr ""
-#: merge-recursive.c:1221
+#: merge-recursive.c:1234
#, c-format
msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
msgstr ""
-#: merge-recursive.c:1251
+#: merge-recursive.c:1264
#, c-format
msgid "Renaming %s to %s and %s to %s instead"
msgstr ""
-#: merge-recursive.c:1450
+#: merge-recursive.c:1463
#, c-format
msgid "CONFLICT (rename/add): Rename %s->%s in %s. %s added in %s"
msgstr ""
-#: merge-recursive.c:1460
+#: merge-recursive.c:1473
#, c-format
msgid "Adding merged %s"
msgstr ""
-#: merge-recursive.c:1465 merge-recursive.c:1663
+#: merge-recursive.c:1478 merge-recursive.c:1676
#, c-format
msgid "Adding as %s instead"
msgstr ""
-#: merge-recursive.c:1516
+#: merge-recursive.c:1529
#, c-format
msgid "cannot read object %s"
msgstr ""
-#: merge-recursive.c:1519
+#: merge-recursive.c:1532
#, c-format
msgid "object %s is not a blob"
msgstr ""
-#: merge-recursive.c:1567
+#: merge-recursive.c:1580
msgid "modify"
msgstr ""
-#: merge-recursive.c:1567
+#: merge-recursive.c:1580
msgid "modified"
msgstr ""
-#: merge-recursive.c:1577
+#: merge-recursive.c:1590
msgid "content"
msgstr ""
-#: merge-recursive.c:1584
+#: merge-recursive.c:1597
msgid "add/add"
msgstr ""
-#: merge-recursive.c:1618
+#: merge-recursive.c:1631
#, c-format
msgid "Skipped %s (merged same as existing)"
msgstr ""
-#: merge-recursive.c:1632
+#: merge-recursive.c:1645
#, c-format
msgid "Auto-merging %s"
msgstr ""
-#: merge-recursive.c:1636 git-submodule.sh:1149
+#: merge-recursive.c:1649 git-submodule.sh:1150
msgid "submodule"
msgstr ""
-#: merge-recursive.c:1637
+#: merge-recursive.c:1650
#, c-format
msgid "CONFLICT (%s): Merge conflict in %s"
msgstr ""
-#: merge-recursive.c:1727
+#: merge-recursive.c:1740
#, c-format
msgid "Removing %s"
msgstr ""
-#: merge-recursive.c:1752
+#: merge-recursive.c:1765
msgid "file/directory"
msgstr ""
-#: merge-recursive.c:1758
+#: merge-recursive.c:1771
msgid "directory/file"
msgstr ""
-#: merge-recursive.c:1763
+#: merge-recursive.c:1776
#, c-format
msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
msgstr ""
-#: merge-recursive.c:1773
+#: merge-recursive.c:1786
#, c-format
msgid "Adding %s"
msgstr ""
-#: merge-recursive.c:1790
+#: merge-recursive.c:1803
msgid "Fatal merge failure, shouldn't happen."
msgstr ""
-#: merge-recursive.c:1809
+#: merge-recursive.c:1822
msgid "Already up-to-date!"
msgstr ""
-#: merge-recursive.c:1818
+#: merge-recursive.c:1831
#, c-format
msgid "merging of trees %s and %s failed"
msgstr ""
-#: merge-recursive.c:1848
+#: merge-recursive.c:1861
#, c-format
msgid "Unprocessed path??? %s"
msgstr ""
-#: merge-recursive.c:1893
+#: merge-recursive.c:1906
msgid "Merging:"
msgstr ""
-#: merge-recursive.c:1906
+#: merge-recursive.c:1919
#, c-format
msgid "found %u common ancestor:"
msgid_plural "found %u common ancestors:"
msgstr[0] ""
msgstr[1] ""
-#: merge-recursive.c:1943
+#: merge-recursive.c:1956
msgid "merge returned no commit"
msgstr ""
-#: merge-recursive.c:2000
+#: merge-recursive.c:2013
#, c-format
msgid "Could not parse object '%s'"
msgstr ""
-#: merge-recursive.c:2012 builtin/merge.c:668
+#: merge-recursive.c:2024 builtin/merge.c:666
msgid "Unable to write index."
msgstr ""
-#: notes-utils.c:40
+#: notes-utils.c:41
msgid "Cannot commit uninitialized/unreferenced notes tree"
msgstr ""
-#: notes-utils.c:81
+#: notes-utils.c:83
#, c-format
msgid "Bad notes.rewriteMode value: '%s'"
msgstr ""
-#: notes-utils.c:91
+#: notes-utils.c:93
#, c-format
msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
msgstr ""
#. TRANSLATORS: The first %s is the name of the
#. environment variable, the second %s is its value
-#: notes-utils.c:118
+#: notes-utils.c:120
#, c-format
msgid "Bad %s value: '%s'"
msgstr ""
-#: object.c:229
+#: object.c:234
#, c-format
msgid "unable to parse object: %s"
msgstr ""
-#: parse-options.c:532
+#: parse-options.c:534
msgid "..."
msgstr ""
-#: parse-options.c:550
+#: parse-options.c:552
#, c-format
msgid "usage: %s"
msgstr ""
#. TRANSLATORS: the colon here should align with the
#. one in "usage: %s" translation
-#: parse-options.c:554
+#: parse-options.c:556
#, c-format
msgid " or: %s"
msgstr ""
-#: parse-options.c:557
+#: parse-options.c:559
#, c-format
msgid " %s"
msgstr ""
-#: parse-options.c:591
+#: parse-options.c:593
msgid "-NUM"
msgstr ""
@@ -899,80 +907,80 @@ msgstr ""
msgid "%s: pathspec magic not supported by this command: %s"
msgstr ""
-#: pathspec.c:433
+#: pathspec.c:432
#, c-format
msgid "pathspec '%s' is beyond a symbolic link"
msgstr ""
-#: pathspec.c:442
+#: pathspec.c:441
msgid ""
"There is nothing to exclude from by :(exclude) patterns.\n"
"Perhaps you forgot to add either ':/' or '.' ?"
msgstr ""
-#: progress.c:224
+#: progress.c:225
msgid "done"
msgstr ""
-#: read-cache.c:1238
+#: read-cache.c:1260
#, c-format
msgid ""
"index.version set, but the value is invalid.\n"
"Using version %i"
msgstr ""
-#: read-cache.c:1248
+#: read-cache.c:1270
#, c-format
msgid ""
"GIT_INDEX_VERSION set, but the value is invalid.\n"
"Using version %i"
msgstr ""
-#: remote.c:758
+#: remote.c:753
#, c-format
msgid "Cannot fetch both %s and %s to %s"
msgstr ""
-#: remote.c:762
+#: remote.c:757
#, c-format
msgid "%s usually tracks %s, not %s"
msgstr ""
-#: remote.c:766
+#: remote.c:761
#, c-format
msgid "%s tracks both %s and %s"
msgstr ""
-#: remote.c:774
+#: remote.c:769
msgid "Internal error"
msgstr ""
-#: remote.c:1948
+#: remote.c:1943
#, c-format
msgid "Your branch is based on '%s', but the upstream is gone.\n"
msgstr ""
-#: remote.c:1952
+#: remote.c:1947
msgid " (use \"git branch --unset-upstream\" to fixup)\n"
msgstr ""
-#: remote.c:1955
+#: remote.c:1950
#, c-format
msgid "Your branch is up-to-date with '%s'.\n"
msgstr ""
-#: remote.c:1959
+#: remote.c:1954
#, c-format
msgid "Your branch is ahead of '%s' by %d commit.\n"
msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
msgstr[0] ""
msgstr[1] ""
-#: remote.c:1965
+#: remote.c:1960
msgid " (use \"git push\" to publish your local commits)\n"
msgstr ""
-#: remote.c:1968
+#: remote.c:1963
#, c-format
msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
msgid_plural ""
@@ -980,11 +988,11 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: remote.c:1976
+#: remote.c:1971
msgid " (use \"git pull\" to update your local branch)\n"
msgstr ""
-#: remote.c:1979
+#: remote.c:1974
#, c-format
msgid ""
"Your branch and '%s' have diverged,\n"
@@ -995,7 +1003,7 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: remote.c:1989
+#: remote.c:1984
msgid " (use \"git pull\" to merge the remote branch into yours)\n"
msgstr ""
@@ -1008,245 +1016,249 @@ msgstr ""
msgid "dup2(%d,%d) failed"
msgstr ""
-#: sequencer.c:206 builtin/merge.c:786 builtin/merge.c:899
-#: builtin/merge.c:1009 builtin/merge.c:1019
+#: sequencer.c:171 builtin/merge.c:782 builtin/merge.c:893
+#: builtin/merge.c:1003 builtin/merge.c:1013
#, c-format
msgid "Could not open '%s' for writing"
msgstr ""
-#: sequencer.c:208 builtin/merge.c:343 builtin/merge.c:789
-#: builtin/merge.c:1011 builtin/merge.c:1024
+#: sequencer.c:173 builtin/merge.c:343 builtin/merge.c:785
+#: builtin/merge.c:1005 builtin/merge.c:1018
#, c-format
msgid "Could not write to '%s'"
msgstr ""
-#: sequencer.c:229
+#: sequencer.c:194
msgid ""
"after resolving the conflicts, mark the corrected paths\n"
"with 'git add <paths>' or 'git rm <paths>'"
msgstr ""
-#: sequencer.c:232
+#: sequencer.c:197
msgid ""
"after resolving the conflicts, mark the corrected paths\n"
"with 'git add <paths>' or 'git rm <paths>'\n"
"and commit the result with 'git commit'"
msgstr ""
-#: sequencer.c:245 sequencer.c:879 sequencer.c:962
+#: sequencer.c:210 sequencer.c:841 sequencer.c:924
#, c-format
msgid "Could not write to %s"
msgstr ""
-#: sequencer.c:248
+#: sequencer.c:213
#, c-format
msgid "Error wrapping up %s"
msgstr ""
-#: sequencer.c:263
+#: sequencer.c:228
msgid "Your local changes would be overwritten by cherry-pick."
msgstr ""
-#: sequencer.c:265
+#: sequencer.c:230
msgid "Your local changes would be overwritten by revert."
msgstr ""
-#: sequencer.c:268
+#: sequencer.c:233
msgid "Commit your changes or stash them to proceed."
msgstr ""
+#: sequencer.c:250
+msgid "Failed to lock HEAD during fast_forward_to"
+msgstr ""
+
#. TRANSLATORS: %s will be "revert" or "cherry-pick"
-#: sequencer.c:325
+#: sequencer.c:293
#, c-format
msgid "%s: Unable to write new index file"
msgstr ""
-#: sequencer.c:356
+#: sequencer.c:324
msgid "Could not resolve HEAD commit\n"
msgstr ""
-#: sequencer.c:378
+#: sequencer.c:344
msgid "Unable to update cache tree\n"
msgstr ""
-#: sequencer.c:430
+#: sequencer.c:391
#, c-format
msgid "Could not parse commit %s\n"
msgstr ""
-#: sequencer.c:435
+#: sequencer.c:396
#, c-format
msgid "Could not parse parent commit %s\n"
msgstr ""
-#: sequencer.c:501
+#: sequencer.c:462
msgid "Your index file is unmerged."
msgstr ""
-#: sequencer.c:520
+#: sequencer.c:481
#, c-format
msgid "Commit %s is a merge but no -m option was given."
msgstr ""
-#: sequencer.c:528
+#: sequencer.c:489
#, c-format
msgid "Commit %s does not have parent %d"
msgstr ""
-#: sequencer.c:532
+#: sequencer.c:493
#, c-format
msgid "Mainline was specified but commit %s is not a merge."
msgstr ""
#. TRANSLATORS: The first %s will be "revert" or
#. "cherry-pick", the second %s a SHA1
-#: sequencer.c:545
+#: sequencer.c:506
#, c-format
msgid "%s: cannot parse parent commit %s"
msgstr ""
-#: sequencer.c:549
+#: sequencer.c:510
#, c-format
msgid "Cannot get commit message for %s"
msgstr ""
-#: sequencer.c:635
+#: sequencer.c:596
#, c-format
msgid "could not revert %s... %s"
msgstr ""
-#: sequencer.c:636
+#: sequencer.c:597
#, c-format
msgid "could not apply %s... %s"
msgstr ""
-#: sequencer.c:672
+#: sequencer.c:633
msgid "empty commit set passed"
msgstr ""
-#: sequencer.c:680
+#: sequencer.c:641
#, c-format
msgid "git %s: failed to read the index"
msgstr ""
-#: sequencer.c:685
+#: sequencer.c:645
#, c-format
msgid "git %s: failed to refresh the index"
msgstr ""
-#: sequencer.c:743
+#: sequencer.c:705
#, c-format
msgid "Cannot %s during a %s"
msgstr ""
-#: sequencer.c:765
+#: sequencer.c:727
#, c-format
msgid "Could not parse line %d."
msgstr ""
-#: sequencer.c:770
+#: sequencer.c:732
msgid "No commits parsed."
msgstr ""
-#: sequencer.c:783
+#: sequencer.c:745
#, c-format
msgid "Could not open %s"
msgstr ""
-#: sequencer.c:787
+#: sequencer.c:749
#, c-format
msgid "Could not read %s."
msgstr ""
-#: sequencer.c:794
+#: sequencer.c:756
#, c-format
msgid "Unusable instruction sheet: %s"
msgstr ""
-#: sequencer.c:824
+#: sequencer.c:786
#, c-format
msgid "Invalid key: %s"
msgstr ""
-#: sequencer.c:827
+#: sequencer.c:789
#, c-format
msgid "Invalid value for %s: %s"
msgstr ""
-#: sequencer.c:839
+#: sequencer.c:801
#, c-format
msgid "Malformed options sheet: %s"
msgstr ""
-#: sequencer.c:860
+#: sequencer.c:822
msgid "a cherry-pick or revert is already in progress"
msgstr ""
-#: sequencer.c:861
+#: sequencer.c:823
msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
msgstr ""
-#: sequencer.c:865
+#: sequencer.c:827
#, c-format
msgid "Could not create sequencer directory %s"
msgstr ""
-#: sequencer.c:881 sequencer.c:966
+#: sequencer.c:843 sequencer.c:928
#, c-format
msgid "Error wrapping up %s."
msgstr ""
-#: sequencer.c:900 sequencer.c:1036
+#: sequencer.c:862 sequencer.c:998
msgid "no cherry-pick or revert in progress"
msgstr ""
-#: sequencer.c:902
+#: sequencer.c:864
msgid "cannot resolve HEAD"
msgstr ""
-#: sequencer.c:904
+#: sequencer.c:866
msgid "cannot abort from a branch yet to be born"
msgstr ""
-#: sequencer.c:926 builtin/apply.c:4061
+#: sequencer.c:888 builtin/apply.c:4062
#, c-format
msgid "cannot open %s: %s"
msgstr ""
-#: sequencer.c:929
+#: sequencer.c:891
#, c-format
msgid "cannot read %s: %s"
msgstr ""
-#: sequencer.c:930
+#: sequencer.c:892
msgid "unexpected end of file"
msgstr ""
-#: sequencer.c:936
+#: sequencer.c:898
#, c-format
msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
msgstr ""
-#: sequencer.c:959
+#: sequencer.c:921
#, c-format
msgid "Could not format %s."
msgstr ""
-#: sequencer.c:1104
+#: sequencer.c:1066
#, c-format
msgid "%s: can't cherry-pick a %s"
msgstr ""
-#: sequencer.c:1107
+#: sequencer.c:1069
#, c-format
msgid "%s: bad revision"
msgstr ""
-#: sequencer.c:1141
+#: sequencer.c:1103
msgid "Can't revert as initial commit"
msgstr ""
-#: sequencer.c:1142
+#: sequencer.c:1104
msgid "Can't cherry-pick into empty head"
msgstr ""
@@ -1263,21 +1275,21 @@ msgid ""
"running \"git config advice.objectNameWarning false\""
msgstr ""
-#: sha1_name.c:1072
+#: sha1_name.c:1060
msgid "HEAD does not point to a branch"
msgstr ""
-#: sha1_name.c:1075
+#: sha1_name.c:1063
#, c-format
msgid "No such branch: '%s'"
msgstr ""
-#: sha1_name.c:1077
+#: sha1_name.c:1065
#, c-format
msgid "No upstream configured for branch '%s'"
msgstr ""
-#: sha1_name.c:1081
+#: sha1_name.c:1069
#, c-format
msgid "Upstream branch '%s' not stored as a remote-tracking branch"
msgstr ""
@@ -1305,17 +1317,17 @@ msgstr ""
msgid "staging updated .gitmodules failed"
msgstr ""
-#: submodule.c:1121 builtin/init-db.c:363
+#: submodule.c:1118 builtin/init-db.c:363
#, c-format
msgid "Could not create git link %s"
msgstr ""
-#: submodule.c:1132
+#: submodule.c:1129
#, c-format
msgid "Could not set core.worktree in %s"
msgstr ""
-#: unpack-trees.c:206
+#: unpack-trees.c:202
msgid "Checking out files"
msgstr ""
@@ -1348,22 +1360,22 @@ msgstr ""
msgid "invalid '..' path segment"
msgstr ""
-#: wrapper.c:422
+#: wrapper.c:460
#, c-format
msgid "unable to access '%s': %s"
msgstr ""
-#: wrapper.c:443
+#: wrapper.c:481
#, c-format
msgid "unable to access '%s'"
msgstr ""
-#: wrapper.c:454
+#: wrapper.c:492
#, c-format
msgid "unable to look up current user in the passwd file: %s"
msgstr ""
-#: wrapper.c:455
+#: wrapper.c:493
msgid "no such user"
msgstr ""
@@ -1504,193 +1516,193 @@ msgstr ""
msgid "bug: unhandled diff status %c"
msgstr ""
-#: wt-status.c:765
+#: wt-status.c:764
msgid "Submodules changed but not updated:"
msgstr ""
-#: wt-status.c:767
+#: wt-status.c:766
msgid "Submodule changes to be committed:"
msgstr ""
-#: wt-status.c:846
+#: wt-status.c:845
msgid ""
"Do not touch the line above.\n"
"Everything below will be removed."
msgstr ""
-#: wt-status.c:937
+#: wt-status.c:936
msgid "You have unmerged paths."
msgstr ""
-#: wt-status.c:940
+#: wt-status.c:939
msgid " (fix conflicts and run \"git commit\")"
msgstr ""
-#: wt-status.c:943
+#: wt-status.c:942
msgid "All conflicts fixed but you are still merging."
msgstr ""
-#: wt-status.c:946
+#: wt-status.c:945
msgid " (use \"git commit\" to conclude merge)"
msgstr ""
-#: wt-status.c:956
+#: wt-status.c:955
msgid "You are in the middle of an am session."
msgstr ""
-#: wt-status.c:959
+#: wt-status.c:958
msgid "The current patch is empty."
msgstr ""
-#: wt-status.c:963
+#: wt-status.c:962
msgid " (fix conflicts and then run \"git am --continue\")"
msgstr ""
-#: wt-status.c:965
+#: wt-status.c:964
msgid " (use \"git am --skip\" to skip this patch)"
msgstr ""
-#: wt-status.c:967
+#: wt-status.c:966
msgid " (use \"git am --abort\" to restore the original branch)"
msgstr ""
-#: wt-status.c:1027 wt-status.c:1044
+#: wt-status.c:1026 wt-status.c:1043
#, c-format
msgid "You are currently rebasing branch '%s' on '%s'."
msgstr ""
-#: wt-status.c:1032 wt-status.c:1049
+#: wt-status.c:1031 wt-status.c:1048
msgid "You are currently rebasing."
msgstr ""
-#: wt-status.c:1035
+#: wt-status.c:1034
msgid " (fix conflicts and then run \"git rebase --continue\")"
msgstr ""
-#: wt-status.c:1037
+#: wt-status.c:1036
msgid " (use \"git rebase --skip\" to skip this patch)"
msgstr ""
-#: wt-status.c:1039
+#: wt-status.c:1038
msgid " (use \"git rebase --abort\" to check out the original branch)"
msgstr ""
-#: wt-status.c:1052
+#: wt-status.c:1051
msgid " (all conflicts fixed: run \"git rebase --continue\")"
msgstr ""
-#: wt-status.c:1056
+#: wt-status.c:1055
#, c-format
msgid ""
"You are currently splitting a commit while rebasing branch '%s' on '%s'."
msgstr ""
-#: wt-status.c:1061
+#: wt-status.c:1060
msgid "You are currently splitting a commit during a rebase."
msgstr ""
-#: wt-status.c:1064
+#: wt-status.c:1063
msgid " (Once your working directory is clean, run \"git rebase --continue\")"
msgstr ""
-#: wt-status.c:1068
+#: wt-status.c:1067
#, c-format
msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
msgstr ""
-#: wt-status.c:1073
+#: wt-status.c:1072
msgid "You are currently editing a commit during a rebase."
msgstr ""
-#: wt-status.c:1076
+#: wt-status.c:1075
msgid " (use \"git commit --amend\" to amend the current commit)"
msgstr ""
-#: wt-status.c:1078
+#: wt-status.c:1077
msgid ""
" (use \"git rebase --continue\" once you are satisfied with your changes)"
msgstr ""
-#: wt-status.c:1088
+#: wt-status.c:1087
#, c-format
msgid "You are currently cherry-picking commit %s."
msgstr ""
-#: wt-status.c:1093
+#: wt-status.c:1092
msgid " (fix conflicts and run \"git cherry-pick --continue\")"
msgstr ""
-#: wt-status.c:1096
+#: wt-status.c:1095
msgid " (all conflicts fixed: run \"git cherry-pick --continue\")"
msgstr ""
-#: wt-status.c:1098
+#: wt-status.c:1097
msgid " (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
msgstr ""
-#: wt-status.c:1107
+#: wt-status.c:1106
#, c-format
msgid "You are currently reverting commit %s."
msgstr ""
-#: wt-status.c:1112
+#: wt-status.c:1111
msgid " (fix conflicts and run \"git revert --continue\")"
msgstr ""
-#: wt-status.c:1115
+#: wt-status.c:1114
msgid " (all conflicts fixed: run \"git revert --continue\")"
msgstr ""
-#: wt-status.c:1117
+#: wt-status.c:1116
msgid " (use \"git revert --abort\" to cancel the revert operation)"
msgstr ""
-#: wt-status.c:1128
+#: wt-status.c:1127
#, c-format
msgid "You are currently bisecting, started from branch '%s'."
msgstr ""
-#: wt-status.c:1132
+#: wt-status.c:1131
msgid "You are currently bisecting."
msgstr ""
-#: wt-status.c:1135
+#: wt-status.c:1134
msgid " (use \"git bisect reset\" to get back to the original branch)"
msgstr ""
-#: wt-status.c:1310
+#: wt-status.c:1309
msgid "On branch "
msgstr ""
-#: wt-status.c:1317
+#: wt-status.c:1316
msgid "rebase in progress; onto "
msgstr ""
-#: wt-status.c:1324
+#: wt-status.c:1323
msgid "HEAD detached at "
msgstr ""
-#: wt-status.c:1326
+#: wt-status.c:1325
msgid "HEAD detached from "
msgstr ""
-#: wt-status.c:1329
+#: wt-status.c:1328
msgid "Not currently on any branch."
msgstr ""
-#: wt-status.c:1346
+#: wt-status.c:1345
msgid "Initial commit"
msgstr ""
-#: wt-status.c:1360
+#: wt-status.c:1359
msgid "Untracked files"
msgstr ""
-#: wt-status.c:1362
+#: wt-status.c:1361
msgid "Ignored files"
msgstr ""
-#: wt-status.c:1366
+#: wt-status.c:1365
#, c-format
msgid ""
"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
@@ -1698,74 +1710,74 @@ msgid ""
"new files yourself (see 'git help status')."
msgstr ""
-#: wt-status.c:1372
+#: wt-status.c:1371
#, c-format
msgid "Untracked files not listed%s"
msgstr ""
-#: wt-status.c:1374
+#: wt-status.c:1373
msgid " (use -u option to show untracked files)"
msgstr ""
-#: wt-status.c:1380
+#: wt-status.c:1379
msgid "No changes"
msgstr ""
-#: wt-status.c:1385
+#: wt-status.c:1384
#, c-format
msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
msgstr ""
-#: wt-status.c:1388
+#: wt-status.c:1387
#, c-format
msgid "no changes added to commit\n"
msgstr ""
-#: wt-status.c:1391
+#: wt-status.c:1390
#, c-format
msgid ""
"nothing added to commit but untracked files present (use \"git add\" to "
"track)\n"
msgstr ""
-#: wt-status.c:1394
+#: wt-status.c:1393
#, c-format
msgid "nothing added to commit but untracked files present\n"
msgstr ""
-#: wt-status.c:1397
+#: wt-status.c:1396
#, c-format
msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
msgstr ""
-#: wt-status.c:1400 wt-status.c:1405
+#: wt-status.c:1399 wt-status.c:1404
#, c-format
msgid "nothing to commit\n"
msgstr ""
-#: wt-status.c:1403
+#: wt-status.c:1402
#, c-format
msgid "nothing to commit (use -u to show untracked files)\n"
msgstr ""
-#: wt-status.c:1407
+#: wt-status.c:1406
#, c-format
msgid "nothing to commit, working directory clean\n"
msgstr ""
-#: wt-status.c:1516
+#: wt-status.c:1515
msgid "HEAD (no branch)"
msgstr ""
-#: wt-status.c:1522
+#: wt-status.c:1521
msgid "Initial commit on "
msgstr ""
-#: wt-status.c:1554
+#: wt-status.c:1553
msgid "gone"
msgstr ""
-#: wt-status.c:1556 wt-status.c:1564
+#: wt-status.c:1555 wt-status.c:1563
msgid "behind "
msgstr ""
@@ -1796,7 +1808,7 @@ msgstr ""
msgid "Unstaged changes after refreshing the index:"
msgstr ""
-#: builtin/add.c:193
+#: builtin/add.c:193 builtin/rev-parse.c:781
msgid "Could not read the index"
msgstr ""
@@ -1827,15 +1839,15 @@ msgstr ""
msgid "The following paths are ignored by one of your .gitignore files:\n"
msgstr ""
-#: builtin/add.c:248 builtin/clean.c:876 builtin/fetch.c:93 builtin/mv.c:70
-#: builtin/prune-packed.c:77 builtin/push.c:489 builtin/remote.c:1344
+#: builtin/add.c:248 builtin/clean.c:875 builtin/fetch.c:108 builtin/mv.c:70
+#: builtin/prune-packed.c:77 builtin/push.c:488 builtin/remote.c:1367
#: builtin/rm.c:269
msgid "dry run"
msgstr ""
-#: builtin/add.c:249 builtin/apply.c:4410 builtin/check-ignore.c:19
-#: builtin/commit.c:1256 builtin/count-objects.c:95 builtin/fsck.c:612
-#: builtin/log.c:1592 builtin/mv.c:69 builtin/read-tree.c:113
+#: builtin/add.c:249 builtin/apply.c:4411 builtin/check-ignore.c:19
+#: builtin/commit.c:1328 builtin/count-objects.c:95 builtin/fsck.c:606
+#: builtin/log.c:1613 builtin/mv.c:69 builtin/read-tree.c:113
msgid "be verbose"
msgstr ""
@@ -1843,7 +1855,7 @@ msgstr ""
msgid "interactive picking"
msgstr ""
-#: builtin/add.c:252 builtin/checkout.c:1108 builtin/reset.c:283
+#: builtin/add.c:252 builtin/checkout.c:1102 builtin/reset.c:285
msgid "select hunks interactively"
msgstr ""
@@ -1896,30 +1908,30 @@ msgstr ""
msgid "adding files failed"
msgstr ""
-#: builtin/add.c:330
+#: builtin/add.c:329
msgid "-A and -u are mutually incompatible"
msgstr ""
-#: builtin/add.c:337
+#: builtin/add.c:336
msgid "Option --ignore-missing can only be used together with --dry-run"
msgstr ""
-#: builtin/add.c:358
+#: builtin/add.c:357
#, c-format
msgid "Nothing specified, nothing added.\n"
msgstr ""
-#: builtin/add.c:359
+#: builtin/add.c:358
#, c-format
msgid "Maybe you wanted to say 'git add .'?\n"
msgstr ""
-#: builtin/add.c:364 builtin/check-ignore.c:172 builtin/clean.c:920
-#: builtin/commit.c:320 builtin/mv.c:90 builtin/reset.c:234 builtin/rm.c:299
+#: builtin/add.c:363 builtin/check-ignore.c:172 builtin/clean.c:919
+#: builtin/commit.c:319 builtin/mv.c:90 builtin/reset.c:234 builtin/rm.c:299
msgid "index file corrupt"
msgstr ""
-#: builtin/add.c:448 builtin/apply.c:4506 builtin/mv.c:280 builtin/rm.c:432
+#: builtin/add.c:446 builtin/apply.c:4506 builtin/mv.c:280 builtin/rm.c:431
msgid "Unable to write new index file"
msgstr ""
@@ -1937,37 +1949,37 @@ msgstr ""
msgid "unrecognized whitespace ignore option '%s'"
msgstr ""
-#: builtin/apply.c:823
+#: builtin/apply.c:825
#, c-format
msgid "Cannot prepare timestamp regexp %s"
msgstr ""
-#: builtin/apply.c:832
+#: builtin/apply.c:834
#, c-format
msgid "regexec returned %d for input: %s"
msgstr ""
-#: builtin/apply.c:913
+#: builtin/apply.c:915
#, c-format
msgid "unable to find filename in patch at line %d"
msgstr ""
-#: builtin/apply.c:945
+#: builtin/apply.c:947
#, c-format
msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
msgstr ""
-#: builtin/apply.c:949
+#: builtin/apply.c:951
#, c-format
msgid "git apply: bad git-diff - inconsistent new filename on line %d"
msgstr ""
-#: builtin/apply.c:950
+#: builtin/apply.c:952
#, c-format
msgid "git apply: bad git-diff - inconsistent old filename on line %d"
msgstr ""
-#: builtin/apply.c:957
+#: builtin/apply.c:959
#, c-format
msgid "git apply: bad git-diff - expected /dev/null on line %d"
msgstr ""
@@ -2075,17 +2087,17 @@ msgstr ""
msgid "missing binary patch data for '%s'"
msgstr ""
-#: builtin/apply.c:2946
+#: builtin/apply.c:2944
#, c-format
msgid "binary patch does not apply to '%s'"
msgstr ""
-#: builtin/apply.c:2952
+#: builtin/apply.c:2950
#, c-format
msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
msgstr ""
-#: builtin/apply.c:2973
+#: builtin/apply.c:2971
#, c-format
msgid "patch failed: %s:%ld"
msgstr ""
@@ -2174,213 +2186,213 @@ msgstr ""
msgid "unable to remove %s from index"
msgstr ""
-#: builtin/apply.c:3851
+#: builtin/apply.c:3852
#, c-format
msgid "corrupt patch for submodule %s"
msgstr ""
-#: builtin/apply.c:3855
+#: builtin/apply.c:3856
#, c-format
msgid "unable to stat newly created file '%s'"
msgstr ""
-#: builtin/apply.c:3860
+#: builtin/apply.c:3861
#, c-format
msgid "unable to create backing store for newly created file %s"
msgstr ""
-#: builtin/apply.c:3863 builtin/apply.c:3971
+#: builtin/apply.c:3864 builtin/apply.c:3972
#, c-format
msgid "unable to add cache entry for %s"
msgstr ""
-#: builtin/apply.c:3896
+#: builtin/apply.c:3897
#, c-format
msgid "closing file '%s'"
msgstr ""
-#: builtin/apply.c:3945
+#: builtin/apply.c:3946
#, c-format
msgid "unable to write file '%s' mode %o"
msgstr ""
-#: builtin/apply.c:4032
+#: builtin/apply.c:4033
#, c-format
msgid "Applied patch %s cleanly."
msgstr ""
-#: builtin/apply.c:4040
+#: builtin/apply.c:4041
msgid "internal error"
msgstr ""
-#: builtin/apply.c:4043
+#: builtin/apply.c:4044
#, c-format
msgid "Applying patch %%s with %d reject..."
msgid_plural "Applying patch %%s with %d rejects..."
msgstr[0] ""
msgstr[1] ""
-#: builtin/apply.c:4053
+#: builtin/apply.c:4054
#, c-format
msgid "truncating .rej filename to %.*s.rej"
msgstr ""
-#: builtin/apply.c:4074
+#: builtin/apply.c:4075
#, c-format
msgid "Hunk #%d applied cleanly."
msgstr ""
-#: builtin/apply.c:4077
+#: builtin/apply.c:4078
#, c-format
msgid "Rejected hunk #%d."
msgstr ""
-#: builtin/apply.c:4227
+#: builtin/apply.c:4228
msgid "unrecognized input"
msgstr ""
-#: builtin/apply.c:4238
+#: builtin/apply.c:4239
msgid "unable to read index file"
msgstr ""
-#: builtin/apply.c:4357 builtin/apply.c:4360 builtin/clone.c:90
-#: builtin/fetch.c:78
+#: builtin/apply.c:4358 builtin/apply.c:4361 builtin/clone.c:90
+#: builtin/fetch.c:93
msgid "path"
msgstr ""
-#: builtin/apply.c:4358
+#: builtin/apply.c:4359
msgid "don't apply changes matching the given path"
msgstr ""
-#: builtin/apply.c:4361
+#: builtin/apply.c:4362
msgid "apply changes matching the given path"
msgstr ""
-#: builtin/apply.c:4363
+#: builtin/apply.c:4364
msgid "num"
msgstr ""
-#: builtin/apply.c:4364
+#: builtin/apply.c:4365
msgid "remove <num> leading slashes from traditional diff paths"
msgstr ""
-#: builtin/apply.c:4367
+#: builtin/apply.c:4368
msgid "ignore additions made by the patch"
msgstr ""
-#: builtin/apply.c:4369
+#: builtin/apply.c:4370
msgid "instead of applying the patch, output diffstat for the input"
msgstr ""
-#: builtin/apply.c:4373
+#: builtin/apply.c:4374
msgid "show number of added and deleted lines in decimal notation"
msgstr ""
-#: builtin/apply.c:4375
+#: builtin/apply.c:4376
msgid "instead of applying the patch, output a summary for the input"
msgstr ""
-#: builtin/apply.c:4377
+#: builtin/apply.c:4378
msgid "instead of applying the patch, see if the patch is applicable"
msgstr ""
-#: builtin/apply.c:4379
+#: builtin/apply.c:4380
msgid "make sure the patch is applicable to the current index"
msgstr ""
-#: builtin/apply.c:4381
+#: builtin/apply.c:4382
msgid "apply a patch without touching the working tree"
msgstr ""
-#: builtin/apply.c:4383
+#: builtin/apply.c:4384
msgid "also apply the patch (use with --stat/--summary/--check)"
msgstr ""
-#: builtin/apply.c:4385
+#: builtin/apply.c:4386
msgid "attempt three-way merge if a patch does not apply"
msgstr ""
-#: builtin/apply.c:4387
+#: builtin/apply.c:4388
msgid "build a temporary index based on embedded index information"
msgstr ""
-#: builtin/apply.c:4389 builtin/checkout-index.c:197 builtin/ls-files.c:455
+#: builtin/apply.c:4390 builtin/checkout-index.c:198 builtin/ls-files.c:455
msgid "paths are separated with NUL character"
msgstr ""
-#: builtin/apply.c:4392
+#: builtin/apply.c:4393
msgid "ensure at least <n> lines of context match"
msgstr ""
-#: builtin/apply.c:4393
+#: builtin/apply.c:4394
msgid "action"
msgstr ""
-#: builtin/apply.c:4394
+#: builtin/apply.c:4395
msgid "detect new or modified lines that have whitespace errors"
msgstr ""
-#: builtin/apply.c:4397 builtin/apply.c:4400
+#: builtin/apply.c:4398 builtin/apply.c:4401
msgid "ignore changes in whitespace when finding context"
msgstr ""
-#: builtin/apply.c:4403
+#: builtin/apply.c:4404
msgid "apply the patch in reverse"
msgstr ""
-#: builtin/apply.c:4405
+#: builtin/apply.c:4406
msgid "don't expect at least one line of context"
msgstr ""
-#: builtin/apply.c:4407
+#: builtin/apply.c:4408
msgid "leave the rejected hunks in corresponding *.rej files"
msgstr ""
-#: builtin/apply.c:4409
+#: builtin/apply.c:4410
msgid "allow overlapping hunks"
msgstr ""
-#: builtin/apply.c:4412
+#: builtin/apply.c:4413
msgid "tolerate incorrectly detected missing new-line at the end of file"
msgstr ""
-#: builtin/apply.c:4415
+#: builtin/apply.c:4416
msgid "do not trust the line counts in the hunk headers"
msgstr ""
-#: builtin/apply.c:4417
+#: builtin/apply.c:4418
msgid "root"
msgstr ""
-#: builtin/apply.c:4418
+#: builtin/apply.c:4419
msgid "prepend <root> to all filenames"
msgstr ""
-#: builtin/apply.c:4440
+#: builtin/apply.c:4441
msgid "--3way outside a repository"
msgstr ""
-#: builtin/apply.c:4448
+#: builtin/apply.c:4449
msgid "--index outside a repository"
msgstr ""
-#: builtin/apply.c:4451
+#: builtin/apply.c:4452
msgid "--cached outside a repository"
msgstr ""
-#: builtin/apply.c:4467
+#: builtin/apply.c:4468
#, c-format
msgid "can't open patch '%s'"
msgstr ""
-#: builtin/apply.c:4481
+#: builtin/apply.c:4482
#, c-format
msgid "squelched %d whitespace error"
msgid_plural "squelched %d whitespace errors"
msgstr[0] ""
msgstr[1] ""
-#: builtin/apply.c:4487 builtin/apply.c:4497
+#: builtin/apply.c:4488 builtin/apply.c:4498
#, c-format
msgid "%d line adds whitespace errors."
msgid_plural "%d lines add whitespace errors."
@@ -2434,106 +2446,116 @@ msgstr ""
msgid "update BISECT_HEAD instead of checking out the current commit"
msgstr ""
-#: builtin/blame.c:27
+#: builtin/blame.c:30
msgid "git blame [options] [rev-opts] [rev] [--] file"
msgstr ""
-#: builtin/blame.c:32
+#: builtin/blame.c:35
msgid "[rev-opts] are documented in git-rev-list(1)"
msgstr ""
-#: builtin/blame.c:2242
+#: builtin/blame.c:2501
msgid "Show blame entries as we find them, incrementally"
msgstr ""
-#: builtin/blame.c:2243
+#: builtin/blame.c:2502
msgid "Show blank SHA-1 for boundary commits (Default: off)"
msgstr ""
-#: builtin/blame.c:2244
+#: builtin/blame.c:2503
msgid "Do not treat root commits as boundaries (Default: off)"
msgstr ""
-#: builtin/blame.c:2245
+#: builtin/blame.c:2504
msgid "Show work cost statistics"
msgstr ""
-#: builtin/blame.c:2246
+#: builtin/blame.c:2505
msgid "Show output score for blame entries"
msgstr ""
-#: builtin/blame.c:2247
+#: builtin/blame.c:2506
msgid "Show original filename (Default: auto)"
msgstr ""
-#: builtin/blame.c:2248
+#: builtin/blame.c:2507
msgid "Show original linenumber (Default: off)"
msgstr ""
-#: builtin/blame.c:2249
+#: builtin/blame.c:2508
msgid "Show in a format designed for machine consumption"
msgstr ""
-#: builtin/blame.c:2250
+#: builtin/blame.c:2509
msgid "Show porcelain format with per-line commit information"
msgstr ""
-#: builtin/blame.c:2251
+#: builtin/blame.c:2510
msgid "Use the same output mode as git-annotate (Default: off)"
msgstr ""
-#: builtin/blame.c:2252
+#: builtin/blame.c:2511
msgid "Show raw timestamp (Default: off)"
msgstr ""
-#: builtin/blame.c:2253
+#: builtin/blame.c:2512
msgid "Show long commit SHA1 (Default: off)"
msgstr ""
-#: builtin/blame.c:2254
+#: builtin/blame.c:2513
msgid "Suppress author name and timestamp (Default: off)"
msgstr ""
-#: builtin/blame.c:2255
+#: builtin/blame.c:2514
msgid "Show author email instead of name (Default: off)"
msgstr ""
-#: builtin/blame.c:2256
+#: builtin/blame.c:2515
msgid "Ignore whitespace differences"
msgstr ""
-#: builtin/blame.c:2257
+#: builtin/blame.c:2516
msgid "Spend extra cycles to find better match"
msgstr ""
-#: builtin/blame.c:2258
+#: builtin/blame.c:2517
msgid "Use revisions from <file> instead of calling git-rev-list"
msgstr ""
-#: builtin/blame.c:2259
+#: builtin/blame.c:2518
msgid "Use <file>'s contents as the final image"
msgstr ""
-#: builtin/blame.c:2260 builtin/blame.c:2261
+#: builtin/blame.c:2519 builtin/blame.c:2520
msgid "score"
msgstr ""
-#: builtin/blame.c:2260
+#: builtin/blame.c:2519
msgid "Find line copies within and across files"
msgstr ""
-#: builtin/blame.c:2261
+#: builtin/blame.c:2520
msgid "Find line movements within and across files"
msgstr ""
-#: builtin/blame.c:2262
+#: builtin/blame.c:2521
msgid "n,m"
msgstr ""
-#: builtin/blame.c:2262
+#: builtin/blame.c:2521
msgid "Process only line range n,m, counting from 1"
msgstr ""
+#. TRANSLATORS: This string is used to tell us the maximum
+#. display width for a relative timestamp in "git blame"
+#. output. For C locale, "4 years, 11 months ago", which
+#. takes 22 places, is the longest among various forms of
+#. relative timestamps, but your language may need more or
+#. fewer display columns.
+#: builtin/blame.c:2599
+msgid "4 years, 11 months ago"
+msgstr ""
+
#: builtin/branch.c:24
msgid "git branch [options] [-r | -a] [--merged | --no-merged]"
msgstr ""
@@ -2766,9 +2788,9 @@ msgid "act on remote-tracking branches"
msgstr ""
#: builtin/branch.c:817 builtin/branch.c:823 builtin/branch.c:844
-#: builtin/branch.c:850 builtin/commit.c:1494 builtin/commit.c:1495
-#: builtin/commit.c:1496 builtin/commit.c:1497 builtin/tag.c:527
-#: builtin/tag.c:533
+#: builtin/branch.c:850 builtin/commit.c:1573 builtin/commit.c:1574
+#: builtin/commit.c:1575 builtin/commit.c:1576 builtin/tag.c:615
+#: builtin/tag.c:621
msgid "commit"
msgstr ""
@@ -2832,7 +2854,7 @@ msgstr ""
msgid "Failed to resolve HEAD as a valid ref."
msgstr ""
-#: builtin/branch.c:872 builtin/clone.c:635
+#: builtin/branch.c:872 builtin/clone.c:636
msgid "HEAD not found below refs/heads!"
msgstr ""
@@ -3009,7 +3031,7 @@ msgstr ""
msgid "terminate input and output records by a NUL character"
msgstr ""
-#: builtin/check-ignore.c:18 builtin/checkout.c:1089 builtin/gc.c:271
+#: builtin/check-ignore.c:18 builtin/checkout.c:1083 builtin/gc.c:285
msgid "suppress progress reporting"
msgstr ""
@@ -3066,43 +3088,43 @@ msgstr ""
msgid "git checkout-index [options] [--] [<file>...]"
msgstr ""
-#: builtin/checkout-index.c:187
+#: builtin/checkout-index.c:188
msgid "check out all files in the index"
msgstr ""
-#: builtin/checkout-index.c:188
+#: builtin/checkout-index.c:189
msgid "force overwrite of existing files"
msgstr ""
-#: builtin/checkout-index.c:190
+#: builtin/checkout-index.c:191
msgid "no warning for existing files and files not in index"
msgstr ""
-#: builtin/checkout-index.c:192
+#: builtin/checkout-index.c:193
msgid "don't checkout new files"
msgstr ""
-#: builtin/checkout-index.c:194
+#: builtin/checkout-index.c:195
msgid "update stat information in the index file"
msgstr ""
-#: builtin/checkout-index.c:200
+#: builtin/checkout-index.c:201
msgid "read list of paths from the standard input"
msgstr ""
-#: builtin/checkout-index.c:202
+#: builtin/checkout-index.c:203
msgid "write the content to temporary files"
msgstr ""
-#: builtin/checkout-index.c:203 builtin/column.c:30
+#: builtin/checkout-index.c:204 builtin/column.c:30
msgid "string"
msgstr ""
-#: builtin/checkout-index.c:204
+#: builtin/checkout-index.c:205
msgid "when creating files, prepend <string>"
msgstr ""
-#: builtin/checkout-index.c:207
+#: builtin/checkout-index.c:208
msgid "copy out the files from named stage"
msgstr ""
@@ -3144,75 +3166,75 @@ msgstr ""
msgid "Unable to add merge result for '%s'"
msgstr ""
-#: builtin/checkout.c:232 builtin/checkout.c:235 builtin/checkout.c:238
-#: builtin/checkout.c:241
+#: builtin/checkout.c:231 builtin/checkout.c:234 builtin/checkout.c:237
+#: builtin/checkout.c:240
#, c-format
msgid "'%s' cannot be used with updating paths"
msgstr ""
-#: builtin/checkout.c:244 builtin/checkout.c:247
+#: builtin/checkout.c:243 builtin/checkout.c:246
#, c-format
msgid "'%s' cannot be used with %s"
msgstr ""
-#: builtin/checkout.c:250
+#: builtin/checkout.c:249
#, c-format
msgid "Cannot update paths and switch to branch '%s' at the same time."
msgstr ""
-#: builtin/checkout.c:261 builtin/checkout.c:450
+#: builtin/checkout.c:260 builtin/checkout.c:449
msgid "corrupt index file"
msgstr ""
-#: builtin/checkout.c:321 builtin/checkout.c:328
+#: builtin/checkout.c:320 builtin/checkout.c:327
#, c-format
msgid "path '%s' is unmerged"
msgstr ""
-#: builtin/checkout.c:472
+#: builtin/checkout.c:471
msgid "you need to resolve your current index first"
msgstr ""
-#: builtin/checkout.c:593
+#: builtin/checkout.c:591
#, c-format
msgid "Can not do reflog for '%s'\n"
msgstr ""
-#: builtin/checkout.c:631
+#: builtin/checkout.c:629
msgid "HEAD is now at"
msgstr ""
-#: builtin/checkout.c:638
+#: builtin/checkout.c:636
#, c-format
msgid "Reset branch '%s'\n"
msgstr ""
-#: builtin/checkout.c:641
+#: builtin/checkout.c:639
#, c-format
msgid "Already on '%s'\n"
msgstr ""
-#: builtin/checkout.c:645
+#: builtin/checkout.c:643
#, c-format
msgid "Switched to and reset branch '%s'\n"
msgstr ""
-#: builtin/checkout.c:647 builtin/checkout.c:1032
+#: builtin/checkout.c:645 builtin/checkout.c:1026
#, c-format
msgid "Switched to a new branch '%s'\n"
msgstr ""
-#: builtin/checkout.c:649
+#: builtin/checkout.c:647
#, c-format
msgid "Switched to branch '%s'\n"
msgstr ""
-#: builtin/checkout.c:705
+#: builtin/checkout.c:699
#, c-format
msgid " ... and %d more.\n"
msgstr ""
-#: builtin/checkout.c:711
+#: builtin/checkout.c:705
#, c-format
msgid ""
"Warning: you are leaving %d commit behind, not connected to\n"
@@ -3227,7 +3249,7 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: builtin/checkout.c:729
+#: builtin/checkout.c:723
#, c-format
msgid ""
"If you want to keep them by creating a new branch, this may be a good time\n"
@@ -3237,151 +3259,151 @@ msgid ""
"\n"
msgstr ""
-#: builtin/checkout.c:759
+#: builtin/checkout.c:753
msgid "internal error in revision walk"
msgstr ""
-#: builtin/checkout.c:763
+#: builtin/checkout.c:757
msgid "Previous HEAD position was"
msgstr ""
-#: builtin/checkout.c:790 builtin/checkout.c:1027
+#: builtin/checkout.c:784 builtin/checkout.c:1021
msgid "You are on a branch yet to be born"
msgstr ""
-#: builtin/checkout.c:934
+#: builtin/checkout.c:928
#, c-format
msgid "only one reference expected, %d given."
msgstr ""
-#: builtin/checkout.c:973
+#: builtin/checkout.c:967
#, c-format
msgid "invalid reference: %s"
msgstr ""
-#: builtin/checkout.c:1002
+#: builtin/checkout.c:996
#, c-format
msgid "reference is not a tree: %s"
msgstr ""
-#: builtin/checkout.c:1041
+#: builtin/checkout.c:1035
msgid "paths cannot be used with switching branches"
msgstr ""
-#: builtin/checkout.c:1044 builtin/checkout.c:1048
+#: builtin/checkout.c:1038 builtin/checkout.c:1042
#, c-format
msgid "'%s' cannot be used with switching branches"
msgstr ""
-#: builtin/checkout.c:1052 builtin/checkout.c:1055 builtin/checkout.c:1060
-#: builtin/checkout.c:1063
+#: builtin/checkout.c:1046 builtin/checkout.c:1049 builtin/checkout.c:1054
+#: builtin/checkout.c:1057
#, c-format
msgid "'%s' cannot be used with '%s'"
msgstr ""
-#: builtin/checkout.c:1068
+#: builtin/checkout.c:1062
#, c-format
msgid "Cannot switch branch to a non-commit '%s'"
msgstr ""
-#: builtin/checkout.c:1090 builtin/checkout.c:1092 builtin/clone.c:88
+#: builtin/checkout.c:1084 builtin/checkout.c:1086 builtin/clone.c:88
#: builtin/remote.c:159 builtin/remote.c:161
msgid "branch"
msgstr ""
-#: builtin/checkout.c:1091
+#: builtin/checkout.c:1085
msgid "create and checkout a new branch"
msgstr ""
-#: builtin/checkout.c:1093
+#: builtin/checkout.c:1087
msgid "create/reset and checkout a branch"
msgstr ""
-#: builtin/checkout.c:1094
+#: builtin/checkout.c:1088
msgid "create reflog for new branch"
msgstr ""
-#: builtin/checkout.c:1095
+#: builtin/checkout.c:1089
msgid "detach the HEAD at named commit"
msgstr ""
-#: builtin/checkout.c:1096
+#: builtin/checkout.c:1090
msgid "set upstream info for new branch"
msgstr ""
-#: builtin/checkout.c:1098
+#: builtin/checkout.c:1092
msgid "new-branch"
msgstr ""
-#: builtin/checkout.c:1098
+#: builtin/checkout.c:1092
msgid "new unparented branch"
msgstr ""
-#: builtin/checkout.c:1099
+#: builtin/checkout.c:1093
msgid "checkout our version for unmerged files"
msgstr ""
-#: builtin/checkout.c:1101
+#: builtin/checkout.c:1095
msgid "checkout their version for unmerged files"
msgstr ""
-#: builtin/checkout.c:1103
+#: builtin/checkout.c:1097
msgid "force checkout (throw away local modifications)"
msgstr ""
-#: builtin/checkout.c:1104
+#: builtin/checkout.c:1098
msgid "perform a 3-way merge with the new branch"
msgstr ""
-#: builtin/checkout.c:1105 builtin/merge.c:225
+#: builtin/checkout.c:1099 builtin/merge.c:225
msgid "update ignored files (default)"
msgstr ""
-#: builtin/checkout.c:1106 builtin/log.c:1228 parse-options.h:245
+#: builtin/checkout.c:1100 builtin/log.c:1236 parse-options.h:245
msgid "style"
msgstr ""
-#: builtin/checkout.c:1107
+#: builtin/checkout.c:1101
msgid "conflict style (merge or diff3)"
msgstr ""
-#: builtin/checkout.c:1110
+#: builtin/checkout.c:1104
msgid "do not limit pathspecs to sparse entries only"
msgstr ""
-#: builtin/checkout.c:1112
+#: builtin/checkout.c:1106
msgid "second guess 'git checkout no-such-branch'"
msgstr ""
-#: builtin/checkout.c:1135
+#: builtin/checkout.c:1129
msgid "-b, -B and --orphan are mutually exclusive"
msgstr ""
-#: builtin/checkout.c:1152
+#: builtin/checkout.c:1146
msgid "--track needs a branch name"
msgstr ""
-#: builtin/checkout.c:1159
+#: builtin/checkout.c:1153
msgid "Missing branch name; try -b"
msgstr ""
-#: builtin/checkout.c:1196
+#: builtin/checkout.c:1190
msgid "invalid path specification"
msgstr ""
-#: builtin/checkout.c:1203
+#: builtin/checkout.c:1197
#, c-format
msgid ""
"Cannot update paths and switch to branch '%s' at the same time.\n"
"Did you intend to checkout '%s' which can not be resolved as commit?"
msgstr ""
-#: builtin/checkout.c:1208
+#: builtin/checkout.c:1202
#, c-format
msgid "git checkout: --detach does not take a path argument '%s'"
msgstr ""
-#: builtin/checkout.c:1212
+#: builtin/checkout.c:1206
msgid ""
"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
"checking out of the index."
@@ -3442,30 +3464,30 @@ msgstr ""
msgid "Huh (%s)?"
msgstr ""
-#: builtin/clean.c:660
+#: builtin/clean.c:659
#, c-format
msgid "Input ignore patterns>> "
msgstr ""
-#: builtin/clean.c:697
+#: builtin/clean.c:696
#, c-format
msgid "WARNING: Cannot find items matched by: %s"
msgstr ""
-#: builtin/clean.c:718
+#: builtin/clean.c:717
msgid "Select items to delete"
msgstr ""
-#: builtin/clean.c:758
+#: builtin/clean.c:757
#, c-format
msgid "remove %s? "
msgstr ""
-#: builtin/clean.c:783
+#: builtin/clean.c:782
msgid "Bye."
msgstr ""
-#: builtin/clean.c:791
+#: builtin/clean.c:790
msgid ""
"clean - start cleaning\n"
"filter by pattern - exclude items from deletion\n"
@@ -3476,68 +3498,68 @@ msgid ""
"? - help for prompt selection"
msgstr ""
-#: builtin/clean.c:818
+#: builtin/clean.c:817
msgid "*** Commands ***"
msgstr ""
-#: builtin/clean.c:819
+#: builtin/clean.c:818
msgid "What now"
msgstr ""
-#: builtin/clean.c:827
+#: builtin/clean.c:826
msgid "Would remove the following item:"
msgid_plural "Would remove the following items:"
msgstr[0] ""
msgstr[1] ""
-#: builtin/clean.c:844
+#: builtin/clean.c:843
msgid "No more files to clean, exiting."
msgstr ""
-#: builtin/clean.c:875
+#: builtin/clean.c:874
msgid "do not print names of files removed"
msgstr ""
-#: builtin/clean.c:877
+#: builtin/clean.c:876
msgid "force"
msgstr ""
-#: builtin/clean.c:878
+#: builtin/clean.c:877
msgid "interactive cleaning"
msgstr ""
-#: builtin/clean.c:880
+#: builtin/clean.c:879
msgid "remove whole directories"
msgstr ""
-#: builtin/clean.c:881 builtin/describe.c:415 builtin/grep.c:716
-#: builtin/ls-files.c:486 builtin/name-rev.c:314 builtin/show-ref.c:185
+#: builtin/clean.c:880 builtin/describe.c:406 builtin/grep.c:714
+#: builtin/ls-files.c:486 builtin/name-rev.c:311 builtin/show-ref.c:185
msgid "pattern"
msgstr ""
-#: builtin/clean.c:882
+#: builtin/clean.c:881
msgid "add <pattern> to ignore rules"
msgstr ""
-#: builtin/clean.c:883
+#: builtin/clean.c:882
msgid "remove ignored files, too"
msgstr ""
-#: builtin/clean.c:885
+#: builtin/clean.c:884
msgid "remove only ignored files"
msgstr ""
-#: builtin/clean.c:903
+#: builtin/clean.c:902
msgid "-x and -X cannot be used together"
msgstr ""
-#: builtin/clean.c:907
+#: builtin/clean.c:906
msgid ""
"clean.requireForce set to true and neither -i, -n, nor -f given; refusing to "
"clean"
msgstr ""
-#: builtin/clean.c:910
+#: builtin/clean.c:909
msgid ""
"clean.requireForce defaults to true and neither -i, -n, nor -f given; "
"refusing to clean"
@@ -3547,8 +3569,8 @@ msgstr ""
msgid "git clone [options] [--] <repo> [<dir>]"
msgstr ""
-#: builtin/clone.c:64 builtin/fetch.c:97 builtin/merge.c:222
-#: builtin/push.c:504
+#: builtin/clone.c:64 builtin/fetch.c:112 builtin/merge.c:222
+#: builtin/push.c:503
msgid "force progress reporting"
msgstr ""
@@ -3608,7 +3630,7 @@ msgstr ""
msgid "path to git-upload-pack on the remote"
msgstr ""
-#: builtin/clone.c:92 builtin/fetch.c:98 builtin/grep.c:661
+#: builtin/clone.c:92 builtin/fetch.c:113 builtin/grep.c:659
msgid "depth"
msgstr ""
@@ -3707,94 +3729,94 @@ msgstr ""
msgid "remote did not send all necessary objects"
msgstr ""
-#: builtin/clone.c:626
+#: builtin/clone.c:627
msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
msgstr ""
-#: builtin/clone.c:657
+#: builtin/clone.c:658
msgid "unable to checkout working tree"
msgstr ""
-#: builtin/clone.c:765
+#: builtin/clone.c:768
msgid "Too many arguments."
msgstr ""
-#: builtin/clone.c:769
+#: builtin/clone.c:772
msgid "You must specify a repository to clone."
msgstr ""
-#: builtin/clone.c:780
+#: builtin/clone.c:783
#, c-format
msgid "--bare and --origin %s options are incompatible."
msgstr ""
-#: builtin/clone.c:783
+#: builtin/clone.c:786
msgid "--bare and --separate-git-dir are incompatible."
msgstr ""
-#: builtin/clone.c:796
+#: builtin/clone.c:799
#, c-format
msgid "repository '%s' does not exist"
msgstr ""
-#: builtin/clone.c:802
-msgid "--depth is ignored in local clones; use file:// instead."
-msgstr ""
-
-#: builtin/clone.c:805
-msgid "source repository is shallow, ignoring --local"
-msgstr ""
-
-#: builtin/clone.c:810
-msgid "--local is ignored"
-msgstr ""
-
-#: builtin/clone.c:814 builtin/fetch.c:1119
+#: builtin/clone.c:805 builtin/fetch.c:1143
#, c-format
msgid "depth %s is not a positive number"
msgstr ""
-#: builtin/clone.c:824
+#: builtin/clone.c:815
#, c-format
msgid "destination path '%s' already exists and is not an empty directory."
msgstr ""
-#: builtin/clone.c:834
+#: builtin/clone.c:825
#, c-format
msgid "working tree '%s' already exists."
msgstr ""
-#: builtin/clone.c:847 builtin/clone.c:859
+#: builtin/clone.c:838 builtin/clone.c:850
#, c-format
msgid "could not create leading directories of '%s'"
msgstr ""
-#: builtin/clone.c:850
+#: builtin/clone.c:841
#, c-format
msgid "could not create work tree dir '%s'."
msgstr ""
-#: builtin/clone.c:869
+#: builtin/clone.c:860
#, c-format
msgid "Cloning into bare repository '%s'...\n"
msgstr ""
-#: builtin/clone.c:871
+#: builtin/clone.c:862
#, c-format
msgid "Cloning into '%s'...\n"
msgstr ""
+#: builtin/clone.c:898
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr ""
+
+#: builtin/clone.c:901
+msgid "source repository is shallow, ignoring --local"
+msgstr ""
+
#: builtin/clone.c:906
+msgid "--local is ignored"
+msgstr ""
+
+#: builtin/clone.c:910
#, c-format
msgid "Don't know how to clone %s"
msgstr ""
-#: builtin/clone.c:957 builtin/clone.c:965
+#: builtin/clone.c:961 builtin/clone.c:969
#, c-format
msgid "Remote branch %s not found in upstream %s"
msgstr ""
-#: builtin/clone.c:968
+#: builtin/clone.c:972
msgid "You appear to have cloned an empty repository."
msgstr ""
@@ -3886,89 +3908,96 @@ msgstr ""
msgid "failed to unpack HEAD tree object"
msgstr ""
-#: builtin/commit.c:330
+#: builtin/commit.c:328
msgid "unable to create temporary index"
msgstr ""
-#: builtin/commit.c:336
+#: builtin/commit.c:334
msgid "interactive add failed"
msgstr ""
-#: builtin/commit.c:369 builtin/commit.c:390 builtin/commit.c:440
+#: builtin/commit.c:366 builtin/commit.c:387 builtin/commit.c:435
msgid "unable to write new_index file"
msgstr ""
-#: builtin/commit.c:421
+#: builtin/commit.c:418
msgid "cannot do a partial commit during a merge."
msgstr ""
-#: builtin/commit.c:423
+#: builtin/commit.c:420
msgid "cannot do a partial commit during a cherry-pick."
msgstr ""
-#: builtin/commit.c:433
+#: builtin/commit.c:429
msgid "cannot read the index"
msgstr ""
-#: builtin/commit.c:453
+#: builtin/commit.c:447
msgid "unable to write temporary index file"
msgstr ""
-#: builtin/commit.c:544 builtin/commit.c:550
+#: builtin/commit.c:557 builtin/commit.c:563
#, c-format
msgid "invalid commit: %s"
msgstr ""
-#: builtin/commit.c:572
+#: builtin/commit.c:585
msgid "malformed --author parameter"
msgstr ""
#: builtin/commit.c:592
#, c-format
+msgid "invalid date format: %s"
+msgstr ""
+
+#: builtin/commit.c:609
+#, c-format
msgid "Malformed ident string: '%s'"
msgstr ""
-#: builtin/commit.c:629 builtin/commit.c:663 builtin/commit.c:1014
+#: builtin/commit.c:642
+msgid ""
+"unable to select a comment character that is not used\n"
+"in the current commit message"
+msgstr ""
+
+#: builtin/commit.c:679 builtin/commit.c:712 builtin/commit.c:1086
#, c-format
msgid "could not lookup commit %s"
msgstr ""
-#: builtin/commit.c:641 builtin/shortlog.c:273
+#: builtin/commit.c:691 builtin/shortlog.c:273
#, c-format
msgid "(reading log message from standard input)\n"
msgstr ""
-#: builtin/commit.c:643
+#: builtin/commit.c:693
msgid "could not read log from standard input"
msgstr ""
-#: builtin/commit.c:647
+#: builtin/commit.c:697
#, c-format
msgid "could not read log file '%s'"
msgstr ""
-#: builtin/commit.c:654
-msgid "commit has empty message"
-msgstr ""
-
-#: builtin/commit.c:670
+#: builtin/commit.c:719
msgid "could not read MERGE_MSG"
msgstr ""
-#: builtin/commit.c:674
+#: builtin/commit.c:723
msgid "could not read SQUASH_MSG"
msgstr ""
-#: builtin/commit.c:678
+#: builtin/commit.c:727
#, c-format
msgid "could not read '%s'"
msgstr ""
-#: builtin/commit.c:749
+#: builtin/commit.c:798
msgid "could not write commit template"
msgstr ""
-#: builtin/commit.c:764
+#: builtin/commit.c:816
#, c-format
msgid ""
"\n"
@@ -3978,7 +4007,7 @@ msgid ""
"and try again.\n"
msgstr ""
-#: builtin/commit.c:769
+#: builtin/commit.c:821
#, c-format
msgid ""
"\n"
@@ -3988,14 +4017,14 @@ msgid ""
"and try again.\n"
msgstr ""
-#: builtin/commit.c:782
+#: builtin/commit.c:834
#, c-format
msgid ""
"Please enter the commit message for your changes. Lines starting\n"
"with '%c' will be ignored, and an empty message aborts the commit.\n"
msgstr ""
-#: builtin/commit.c:789
+#: builtin/commit.c:841
#, c-format
msgid ""
"Please enter the commit message for your changes. Lines starting\n"
@@ -4003,351 +4032,356 @@ msgid ""
"An empty message aborts the commit.\n"
msgstr ""
-#: builtin/commit.c:802
+#: builtin/commit.c:855
+#, c-format
+msgid "%sAuthor: %.*s <%.*s>"
+msgstr ""
+
+#: builtin/commit.c:863
#, c-format
-msgid "%sAuthor: %s"
+msgid "%sDate: %s"
msgstr ""
-#: builtin/commit.c:809
+#: builtin/commit.c:870
#, c-format
-msgid "%sCommitter: %s"
+msgid "%sCommitter: %.*s <%.*s>"
msgstr ""
-#: builtin/commit.c:829
+#: builtin/commit.c:888
msgid "Cannot read index"
msgstr ""
-#: builtin/commit.c:872
+#: builtin/commit.c:945
msgid "Error building trees"
msgstr ""
-#: builtin/commit.c:887 builtin/tag.c:391
+#: builtin/commit.c:960 builtin/tag.c:495
#, c-format
msgid "Please supply the message using either -m or -F option.\n"
msgstr ""
-#: builtin/commit.c:989
+#: builtin/commit.c:1061
#, c-format
msgid "No existing author found with '%s'"
msgstr ""
-#: builtin/commit.c:1004 builtin/commit.c:1244
+#: builtin/commit.c:1076 builtin/commit.c:1316
#, c-format
msgid "Invalid untracked files mode '%s'"
msgstr ""
-#: builtin/commit.c:1041
+#: builtin/commit.c:1113
msgid "--long and -z are incompatible"
msgstr ""
-#: builtin/commit.c:1071
+#: builtin/commit.c:1143
msgid "Using both --reset-author and --author does not make sense"
msgstr ""
-#: builtin/commit.c:1080
+#: builtin/commit.c:1152
msgid "You have nothing to amend."
msgstr ""
-#: builtin/commit.c:1083
+#: builtin/commit.c:1155
msgid "You are in the middle of a merge -- cannot amend."
msgstr ""
-#: builtin/commit.c:1085
+#: builtin/commit.c:1157
msgid "You are in the middle of a cherry-pick -- cannot amend."
msgstr ""
-#: builtin/commit.c:1088
+#: builtin/commit.c:1160
msgid "Options --squash and --fixup cannot be used together"
msgstr ""
-#: builtin/commit.c:1098
+#: builtin/commit.c:1170
msgid "Only one of -c/-C/-F/--fixup can be used."
msgstr ""
-#: builtin/commit.c:1100
+#: builtin/commit.c:1172
msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
msgstr ""
-#: builtin/commit.c:1108
+#: builtin/commit.c:1180
msgid "--reset-author can be used only with -C, -c or --amend."
msgstr ""
-#: builtin/commit.c:1125
+#: builtin/commit.c:1197
msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
msgstr ""
-#: builtin/commit.c:1127
+#: builtin/commit.c:1199
msgid "No paths with --include/--only does not make sense."
msgstr ""
-#: builtin/commit.c:1129
+#: builtin/commit.c:1201
msgid "Clever... amending the last one with dirty index."
msgstr ""
-#: builtin/commit.c:1131
+#: builtin/commit.c:1203
msgid "Explicit paths specified without -i or -o; assuming --only paths..."
msgstr ""
-#: builtin/commit.c:1143 builtin/tag.c:639
+#: builtin/commit.c:1215 builtin/tag.c:727
#, c-format
msgid "Invalid cleanup mode %s"
msgstr ""
-#: builtin/commit.c:1148
+#: builtin/commit.c:1220
msgid "Paths with -a does not make sense."
msgstr ""
-#: builtin/commit.c:1258 builtin/commit.c:1516
+#: builtin/commit.c:1330 builtin/commit.c:1595
msgid "show status concisely"
msgstr ""
-#: builtin/commit.c:1260 builtin/commit.c:1518
+#: builtin/commit.c:1332 builtin/commit.c:1597
msgid "show branch information"
msgstr ""
-#: builtin/commit.c:1262 builtin/commit.c:1520 builtin/push.c:490
+#: builtin/commit.c:1334 builtin/commit.c:1599 builtin/push.c:489
msgid "machine-readable output"
msgstr ""
-#: builtin/commit.c:1265 builtin/commit.c:1522
+#: builtin/commit.c:1337 builtin/commit.c:1601
msgid "show status in long format (default)"
msgstr ""
-#: builtin/commit.c:1268 builtin/commit.c:1525
+#: builtin/commit.c:1340 builtin/commit.c:1604
msgid "terminate entries with NUL"
msgstr ""
-#: builtin/commit.c:1270 builtin/commit.c:1528 builtin/fast-export.c:674
-#: builtin/fast-export.c:677 builtin/tag.c:514
+#: builtin/commit.c:1342 builtin/commit.c:1607 builtin/fast-export.c:703
+#: builtin/fast-export.c:706 builtin/tag.c:602
msgid "mode"
msgstr ""
-#: builtin/commit.c:1271 builtin/commit.c:1528
+#: builtin/commit.c:1343 builtin/commit.c:1607
msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
msgstr ""
-#: builtin/commit.c:1274
+#: builtin/commit.c:1346
msgid "show ignored files"
msgstr ""
-#: builtin/commit.c:1275 parse-options.h:153
+#: builtin/commit.c:1347 parse-options.h:153
msgid "when"
msgstr ""
-#: builtin/commit.c:1276
+#: builtin/commit.c:1348
msgid ""
"ignore changes to submodules, optional when: all, dirty, untracked. "
"(Default: all)"
msgstr ""
-#: builtin/commit.c:1278
+#: builtin/commit.c:1350
msgid "list untracked files in columns"
msgstr ""
-#: builtin/commit.c:1347
+#: builtin/commit.c:1419
msgid "couldn't look up newly created commit"
msgstr ""
-#: builtin/commit.c:1349
+#: builtin/commit.c:1421
msgid "could not parse newly created commit"
msgstr ""
-#: builtin/commit.c:1390
+#: builtin/commit.c:1469
msgid "detached HEAD"
msgstr ""
-#: builtin/commit.c:1392
+#: builtin/commit.c:1471
msgid " (root-commit)"
msgstr ""
-#: builtin/commit.c:1486
+#: builtin/commit.c:1565
msgid "suppress summary after successful commit"
msgstr ""
-#: builtin/commit.c:1487
+#: builtin/commit.c:1566
msgid "show diff in commit message template"
msgstr ""
-#: builtin/commit.c:1489
+#: builtin/commit.c:1568
msgid "Commit message options"
msgstr ""
-#: builtin/commit.c:1490 builtin/tag.c:512
+#: builtin/commit.c:1569 builtin/tag.c:600
msgid "read message from file"
msgstr ""
-#: builtin/commit.c:1491
+#: builtin/commit.c:1570
msgid "author"
msgstr ""
-#: builtin/commit.c:1491
+#: builtin/commit.c:1570
msgid "override author for commit"
msgstr ""
-#: builtin/commit.c:1492 builtin/gc.c:272
+#: builtin/commit.c:1571 builtin/gc.c:286
msgid "date"
msgstr ""
-#: builtin/commit.c:1492
+#: builtin/commit.c:1571
msgid "override date for commit"
msgstr ""
-#: builtin/commit.c:1493 builtin/merge.c:216 builtin/notes.c:409
-#: builtin/notes.c:566 builtin/tag.c:510
+#: builtin/commit.c:1572 builtin/merge.c:216 builtin/notes.c:409
+#: builtin/notes.c:566 builtin/tag.c:598
msgid "message"
msgstr ""
-#: builtin/commit.c:1493
+#: builtin/commit.c:1572
msgid "commit message"
msgstr ""
-#: builtin/commit.c:1494
+#: builtin/commit.c:1573
msgid "reuse and edit message from specified commit"
msgstr ""
-#: builtin/commit.c:1495
+#: builtin/commit.c:1574
msgid "reuse message from specified commit"
msgstr ""
-#: builtin/commit.c:1496
+#: builtin/commit.c:1575
msgid "use autosquash formatted message to fixup specified commit"
msgstr ""
-#: builtin/commit.c:1497
+#: builtin/commit.c:1576
msgid "use autosquash formatted message to squash specified commit"
msgstr ""
-#: builtin/commit.c:1498
+#: builtin/commit.c:1577
msgid "the commit is authored by me now (used with -C/-c/--amend)"
msgstr ""
-#: builtin/commit.c:1499 builtin/log.c:1180 builtin/revert.c:86
+#: builtin/commit.c:1578 builtin/log.c:1188 builtin/revert.c:86
msgid "add Signed-off-by:"
msgstr ""
-#: builtin/commit.c:1500
+#: builtin/commit.c:1579
msgid "use specified template file"
msgstr ""
-#: builtin/commit.c:1501
+#: builtin/commit.c:1580
msgid "force edit of commit"
msgstr ""
-#: builtin/commit.c:1502
+#: builtin/commit.c:1581
msgid "default"
msgstr ""
-#: builtin/commit.c:1502 builtin/tag.c:515
+#: builtin/commit.c:1581 builtin/tag.c:603
msgid "how to strip spaces and #comments from message"
msgstr ""
-#: builtin/commit.c:1503
+#: builtin/commit.c:1582
msgid "include status in commit message template"
msgstr ""
-#: builtin/commit.c:1504 builtin/merge.c:223 builtin/revert.c:92
-#: builtin/tag.c:516
+#: builtin/commit.c:1583 builtin/merge.c:223 builtin/revert.c:92
+#: builtin/tag.c:604
msgid "key-id"
msgstr ""
-#: builtin/commit.c:1505 builtin/merge.c:224 builtin/revert.c:93
+#: builtin/commit.c:1584 builtin/merge.c:224 builtin/revert.c:93
msgid "GPG sign commit"
msgstr ""
-#: builtin/commit.c:1508
+#: builtin/commit.c:1587
msgid "Commit contents options"
msgstr ""
-#: builtin/commit.c:1509
+#: builtin/commit.c:1588
msgid "commit all changed files"
msgstr ""
-#: builtin/commit.c:1510
+#: builtin/commit.c:1589
msgid "add specified files to index for commit"
msgstr ""
-#: builtin/commit.c:1511
+#: builtin/commit.c:1590
msgid "interactively add files"
msgstr ""
-#: builtin/commit.c:1512
+#: builtin/commit.c:1591
msgid "interactively add changes"
msgstr ""
-#: builtin/commit.c:1513
+#: builtin/commit.c:1592
msgid "commit only specified files"
msgstr ""
-#: builtin/commit.c:1514
+#: builtin/commit.c:1593
msgid "bypass pre-commit hook"
msgstr ""
-#: builtin/commit.c:1515
+#: builtin/commit.c:1594
msgid "show what would be committed"
msgstr ""
-#: builtin/commit.c:1526
+#: builtin/commit.c:1605
msgid "amend previous commit"
msgstr ""
-#: builtin/commit.c:1527
+#: builtin/commit.c:1606
msgid "bypass post-rewrite hook"
msgstr ""
-#: builtin/commit.c:1532
+#: builtin/commit.c:1611
msgid "ok to record an empty change"
msgstr ""
-#: builtin/commit.c:1534
+#: builtin/commit.c:1613
msgid "ok to record a change with an empty message"
msgstr ""
-#: builtin/commit.c:1562
+#: builtin/commit.c:1641
msgid "could not parse HEAD commit"
msgstr ""
-#: builtin/commit.c:1601 builtin/merge.c:518
+#: builtin/commit.c:1680 builtin/merge.c:518
#, c-format
msgid "could not open '%s' for reading"
msgstr ""
-#: builtin/commit.c:1608
+#: builtin/commit.c:1687
#, c-format
msgid "Corrupt MERGE_HEAD file (%s)"
msgstr ""
-#: builtin/commit.c:1615
+#: builtin/commit.c:1694
msgid "could not read MERGE_MODE"
msgstr ""
-#: builtin/commit.c:1634
+#: builtin/commit.c:1713
#, c-format
msgid "could not read commit message: %s"
msgstr ""
-#: builtin/commit.c:1645
+#: builtin/commit.c:1724
#, c-format
msgid "Aborting commit; you did not edit the message.\n"
msgstr ""
-#: builtin/commit.c:1650
+#: builtin/commit.c:1729
#, c-format
msgid "Aborting commit due to empty commit message.\n"
msgstr ""
-#: builtin/commit.c:1665 builtin/merge.c:857 builtin/merge.c:882
+#: builtin/commit.c:1744 builtin/merge.c:851 builtin/merge.c:876
msgid "failed to write commit object"
msgstr ""
-#: builtin/commit.c:1686
+#: builtin/commit.c:1756
msgid "cannot lock HEAD ref"
msgstr ""
-#: builtin/commit.c:1690
+#: builtin/commit.c:1769
msgid "cannot update HEAD ref"
msgstr ""
-#: builtin/commit.c:1701
+#: builtin/commit.c:1780
msgid ""
"Repository has been updated, but unable to write\n"
"new_index file. Check that disk is not full or quota is\n"
@@ -4498,129 +4532,129 @@ msgstr ""
msgid "git describe [options] --dirty"
msgstr ""
-#: builtin/describe.c:225
+#: builtin/describe.c:216
#, c-format
msgid "annotated tag %s not available"
msgstr ""
-#: builtin/describe.c:229
+#: builtin/describe.c:220
#, c-format
msgid "annotated tag %s has no embedded name"
msgstr ""
-#: builtin/describe.c:231
+#: builtin/describe.c:222
#, c-format
msgid "tag '%s' is really '%s' here"
msgstr ""
-#: builtin/describe.c:258
+#: builtin/describe.c:249
#, c-format
msgid "Not a valid object name %s"
msgstr ""
-#: builtin/describe.c:261
+#: builtin/describe.c:252
#, c-format
msgid "%s is not a valid '%s' object"
msgstr ""
-#: builtin/describe.c:278
+#: builtin/describe.c:269
#, c-format
msgid "no tag exactly matches '%s'"
msgstr ""
-#: builtin/describe.c:280
+#: builtin/describe.c:271
#, c-format
msgid "searching to describe %s\n"
msgstr ""
-#: builtin/describe.c:327
+#: builtin/describe.c:318
#, c-format
msgid "finished search at %s\n"
msgstr ""
-#: builtin/describe.c:354
+#: builtin/describe.c:345
#, c-format
msgid ""
"No annotated tags can describe '%s'.\n"
"However, there were unannotated tags: try --tags."
msgstr ""
-#: builtin/describe.c:358
+#: builtin/describe.c:349
#, c-format
msgid ""
"No tags can describe '%s'.\n"
"Try --always, or create some tags."
msgstr ""
-#: builtin/describe.c:379
+#: builtin/describe.c:370
#, c-format
msgid "traversed %lu commits\n"
msgstr ""
-#: builtin/describe.c:382
+#: builtin/describe.c:373
#, c-format
msgid ""
"more than %i tags found; listed %i most recent\n"
"gave up search at %s\n"
msgstr ""
-#: builtin/describe.c:404
+#: builtin/describe.c:395
msgid "find the tag that comes after the commit"
msgstr ""
-#: builtin/describe.c:405
+#: builtin/describe.c:396
msgid "debug search strategy on stderr"
msgstr ""
-#: builtin/describe.c:406
+#: builtin/describe.c:397
msgid "use any ref"
msgstr ""
-#: builtin/describe.c:407
+#: builtin/describe.c:398
msgid "use any tag, even unannotated"
msgstr ""
-#: builtin/describe.c:408
+#: builtin/describe.c:399
msgid "always use long format"
msgstr ""
-#: builtin/describe.c:409
+#: builtin/describe.c:400
msgid "only follow first parent"
msgstr ""
-#: builtin/describe.c:412
+#: builtin/describe.c:403
msgid "only output exact matches"
msgstr ""
-#: builtin/describe.c:414
+#: builtin/describe.c:405
msgid "consider <n> most recent tags (default: 10)"
msgstr ""
-#: builtin/describe.c:416
+#: builtin/describe.c:407
msgid "only consider tags matching <pattern>"
msgstr ""
-#: builtin/describe.c:418 builtin/name-rev.c:321
+#: builtin/describe.c:409 builtin/name-rev.c:318
msgid "show abbreviated commit object as fallback"
msgstr ""
-#: builtin/describe.c:419
+#: builtin/describe.c:410
msgid "mark"
msgstr ""
-#: builtin/describe.c:420
+#: builtin/describe.c:411
msgid "append <mark> on dirty working tree (default: \"-dirty\")"
msgstr ""
-#: builtin/describe.c:438
+#: builtin/describe.c:429
msgid "--long is incompatible with --abbrev=0"
msgstr ""
-#: builtin/describe.c:464
+#: builtin/describe.c:455
msgid "No names found, cannot describe anything."
msgstr ""
-#: builtin/describe.c:484
+#: builtin/describe.c:475
msgid "--dirty is incompatible with commit-ishes"
msgstr ""
@@ -4653,46 +4687,54 @@ msgstr ""
msgid "unhandled object '%s' given."
msgstr ""
-#: builtin/fast-export.c:22
+#: builtin/fast-export.c:23
msgid "git fast-export [rev-list-opts]"
msgstr ""
-#: builtin/fast-export.c:673
+#: builtin/fast-export.c:702
msgid "show progress after <n> objects"
msgstr ""
-#: builtin/fast-export.c:675
+#: builtin/fast-export.c:704
msgid "select handling of signed tags"
msgstr ""
-#: builtin/fast-export.c:678
+#: builtin/fast-export.c:707
msgid "select handling of tags that tag filtered objects"
msgstr ""
-#: builtin/fast-export.c:681
+#: builtin/fast-export.c:710
msgid "Dump marks to this file"
msgstr ""
-#: builtin/fast-export.c:683
+#: builtin/fast-export.c:712
msgid "Import marks from this file"
msgstr ""
-#: builtin/fast-export.c:685
+#: builtin/fast-export.c:714
msgid "Fake a tagger when tags lack one"
msgstr ""
-#: builtin/fast-export.c:687
+#: builtin/fast-export.c:716
msgid "Output full tree for each commit"
msgstr ""
-#: builtin/fast-export.c:689
+#: builtin/fast-export.c:718
msgid "Use the done feature to terminate the stream"
msgstr ""
-#: builtin/fast-export.c:690
+#: builtin/fast-export.c:719
msgid "Skip output of blob data"
msgstr ""
+#: builtin/fast-export.c:720
+msgid "refspec"
+msgstr ""
+
+#: builtin/fast-export.c:721
+msgid "Apply refspec to exported refs"
+msgstr ""
+
#: builtin/fetch.c:20
msgid "git fetch [<options>] [<repository> [<refspec>...]]"
msgstr ""
@@ -4709,239 +4751,247 @@ msgstr ""
msgid "git fetch --all [<options>]"
msgstr ""
-#: builtin/fetch.c:75
+#: builtin/fetch.c:90
msgid "fetch from all remotes"
msgstr ""
-#: builtin/fetch.c:77
+#: builtin/fetch.c:92
msgid "append to .git/FETCH_HEAD instead of overwriting"
msgstr ""
-#: builtin/fetch.c:79
+#: builtin/fetch.c:94
msgid "path to upload pack on remote end"
msgstr ""
-#: builtin/fetch.c:80
+#: builtin/fetch.c:95
msgid "force overwrite of local branch"
msgstr ""
-#: builtin/fetch.c:82
+#: builtin/fetch.c:97
msgid "fetch from multiple remotes"
msgstr ""
-#: builtin/fetch.c:84
+#: builtin/fetch.c:99
msgid "fetch all tags and associated objects"
msgstr ""
-#: builtin/fetch.c:86
+#: builtin/fetch.c:101
msgid "do not fetch all tags (--no-tags)"
msgstr ""
-#: builtin/fetch.c:88
+#: builtin/fetch.c:103
msgid "prune remote-tracking branches no longer on remote"
msgstr ""
-#: builtin/fetch.c:89
+#: builtin/fetch.c:104
msgid "on-demand"
msgstr ""
-#: builtin/fetch.c:90
+#: builtin/fetch.c:105
msgid "control recursive fetching of submodules"
msgstr ""
-#: builtin/fetch.c:94
+#: builtin/fetch.c:109
msgid "keep downloaded pack"
msgstr ""
-#: builtin/fetch.c:96
+#: builtin/fetch.c:111
msgid "allow updating of HEAD ref"
msgstr ""
-#: builtin/fetch.c:99
+#: builtin/fetch.c:114
msgid "deepen history of shallow clone"
msgstr ""
-#: builtin/fetch.c:101
+#: builtin/fetch.c:116
msgid "convert to a complete repository"
msgstr ""
-#: builtin/fetch.c:103 builtin/log.c:1197
+#: builtin/fetch.c:118 builtin/log.c:1205
msgid "dir"
msgstr ""
-#: builtin/fetch.c:104
+#: builtin/fetch.c:119
msgid "prepend this to submodule path output"
msgstr ""
-#: builtin/fetch.c:107
+#: builtin/fetch.c:122
msgid "default mode for recursion"
msgstr ""
-#: builtin/fetch.c:109
+#: builtin/fetch.c:124
msgid "accept refs that update .git/shallow"
msgstr ""
-#: builtin/fetch.c:347
+#: builtin/fetch.c:125
+msgid "refmap"
+msgstr ""
+
+#: builtin/fetch.c:126
+msgid "specify fetch refmap"
+msgstr ""
+
+#: builtin/fetch.c:376
msgid "Couldn't find remote ref HEAD"
msgstr ""
-#: builtin/fetch.c:411
+#: builtin/fetch.c:440
#, c-format
msgid "object %s not found"
msgstr ""
-#: builtin/fetch.c:416
+#: builtin/fetch.c:445
msgid "[up to date]"
msgstr ""
-#: builtin/fetch.c:430
+#: builtin/fetch.c:459
#, c-format
msgid "! %-*s %-*s -> %s (can't fetch in current branch)"
msgstr ""
-#: builtin/fetch.c:431 builtin/fetch.c:517
+#: builtin/fetch.c:460 builtin/fetch.c:546
msgid "[rejected]"
msgstr ""
-#: builtin/fetch.c:442
+#: builtin/fetch.c:471
msgid "[tag update]"
msgstr ""
-#: builtin/fetch.c:444 builtin/fetch.c:479 builtin/fetch.c:497
+#: builtin/fetch.c:473 builtin/fetch.c:508 builtin/fetch.c:526
msgid " (unable to update local ref)"
msgstr ""
-#: builtin/fetch.c:462
+#: builtin/fetch.c:491
msgid "[new tag]"
msgstr ""
-#: builtin/fetch.c:465
+#: builtin/fetch.c:494
msgid "[new branch]"
msgstr ""
-#: builtin/fetch.c:468
+#: builtin/fetch.c:497
msgid "[new ref]"
msgstr ""
-#: builtin/fetch.c:513
+#: builtin/fetch.c:542
msgid "unable to update local ref"
msgstr ""
-#: builtin/fetch.c:513
+#: builtin/fetch.c:542
msgid "forced update"
msgstr ""
-#: builtin/fetch.c:519
+#: builtin/fetch.c:548
msgid "(non-fast-forward)"
msgstr ""
-#: builtin/fetch.c:552 builtin/fetch.c:785
+#: builtin/fetch.c:581 builtin/fetch.c:814
#, c-format
msgid "cannot open %s: %s\n"
msgstr ""
-#: builtin/fetch.c:561
+#: builtin/fetch.c:590
#, c-format
msgid "%s did not send all necessary objects\n"
msgstr ""
-#: builtin/fetch.c:579
+#: builtin/fetch.c:608
#, c-format
msgid "reject %s because shallow roots are not allowed to be updated"
msgstr ""
-#: builtin/fetch.c:667 builtin/fetch.c:750
+#: builtin/fetch.c:696 builtin/fetch.c:779
#, c-format
msgid "From %.*s\n"
msgstr ""
-#: builtin/fetch.c:678
+#: builtin/fetch.c:707
#, c-format
msgid ""
"some local refs could not be updated; try running\n"
" 'git remote prune %s' to remove any old, conflicting branches"
msgstr ""
-#: builtin/fetch.c:730
+#: builtin/fetch.c:759
#, c-format
msgid " (%s will become dangling)"
msgstr ""
-#: builtin/fetch.c:731
+#: builtin/fetch.c:760
#, c-format
msgid " (%s has become dangling)"
msgstr ""
-#: builtin/fetch.c:755
+#: builtin/fetch.c:784
msgid "[deleted]"
msgstr ""
-#: builtin/fetch.c:756 builtin/remote.c:1050
+#: builtin/fetch.c:785 builtin/remote.c:1059
msgid "(none)"
msgstr ""
-#: builtin/fetch.c:775
+#: builtin/fetch.c:804
#, c-format
msgid "Refusing to fetch into current branch %s of non-bare repository"
msgstr ""
-#: builtin/fetch.c:794
+#: builtin/fetch.c:823
#, c-format
msgid "Option \"%s\" value \"%s\" is not valid for %s"
msgstr ""
-#: builtin/fetch.c:797
+#: builtin/fetch.c:826
#, c-format
msgid "Option \"%s\" is ignored for %s\n"
msgstr ""
-#: builtin/fetch.c:853
+#: builtin/fetch.c:882
#, c-format
msgid "Don't know how to fetch from %s"
msgstr ""
-#: builtin/fetch.c:1015
+#: builtin/fetch.c:1044
#, c-format
msgid "Fetching %s\n"
msgstr ""
-#: builtin/fetch.c:1017 builtin/remote.c:90
+#: builtin/fetch.c:1046 builtin/remote.c:90
#, c-format
msgid "Could not fetch %s"
msgstr ""
-#: builtin/fetch.c:1035
+#: builtin/fetch.c:1064
msgid ""
"No remote repository specified. Please, specify either a URL or a\n"
"remote name from which new revisions should be fetched."
msgstr ""
-#: builtin/fetch.c:1059
+#: builtin/fetch.c:1087
msgid "You need to specify a tag name."
msgstr ""
-#: builtin/fetch.c:1107
+#: builtin/fetch.c:1131
msgid "--depth and --unshallow cannot be used together"
msgstr ""
-#: builtin/fetch.c:1109
+#: builtin/fetch.c:1133
msgid "--unshallow on a complete repository does not make sense"
msgstr ""
-#: builtin/fetch.c:1132
+#: builtin/fetch.c:1156
msgid "fetch --all does not take a repository argument"
msgstr ""
-#: builtin/fetch.c:1134
+#: builtin/fetch.c:1158
msgid "fetch --all does not make sense with refspecs"
msgstr ""
-#: builtin/fetch.c:1145
+#: builtin/fetch.c:1169
#, c-format
msgid "No such remote or remote group: %s"
msgstr ""
-#: builtin/fetch.c:1153
+#: builtin/fetch.c:1177
msgid "Fetching a group and specifying refspecs does not make sense"
msgstr ""
@@ -4949,9 +4999,9 @@ msgstr ""
msgid "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]"
msgstr ""
-#: builtin/fmt-merge-msg.c:663 builtin/fmt-merge-msg.c:666 builtin/grep.c:700
-#: builtin/merge.c:196 builtin/repack.c:175 builtin/repack.c:179
-#: builtin/show-branch.c:654 builtin/show-ref.c:178 builtin/tag.c:501
+#: builtin/fmt-merge-msg.c:663 builtin/fmt-merge-msg.c:666 builtin/grep.c:698
+#: builtin/merge.c:196 builtin/repack.c:179 builtin/repack.c:183
+#: builtin/show-branch.c:654 builtin/show-ref.c:178 builtin/tag.c:589
#: parse-options.h:132 parse-options.h:239
msgid "n"
msgstr ""
@@ -4976,43 +5026,43 @@ msgstr ""
msgid "file to read from"
msgstr ""
-#: builtin/for-each-ref.c:1063
+#: builtin/for-each-ref.c:1051
msgid "git for-each-ref [options] [<pattern>]"
msgstr ""
-#: builtin/for-each-ref.c:1078
+#: builtin/for-each-ref.c:1066
msgid "quote placeholders suitably for shells"
msgstr ""
-#: builtin/for-each-ref.c:1080
+#: builtin/for-each-ref.c:1068
msgid "quote placeholders suitably for perl"
msgstr ""
-#: builtin/for-each-ref.c:1082
+#: builtin/for-each-ref.c:1070
msgid "quote placeholders suitably for python"
msgstr ""
-#: builtin/for-each-ref.c:1084
+#: builtin/for-each-ref.c:1072
msgid "quote placeholders suitably for tcl"
msgstr ""
-#: builtin/for-each-ref.c:1087
+#: builtin/for-each-ref.c:1075
msgid "show only <n> matched refs"
msgstr ""
-#: builtin/for-each-ref.c:1088 builtin/replace.c:177
+#: builtin/for-each-ref.c:1076 builtin/replace.c:435
msgid "format"
msgstr ""
-#: builtin/for-each-ref.c:1088
+#: builtin/for-each-ref.c:1076
msgid "format to use for the output"
msgstr ""
-#: builtin/for-each-ref.c:1089
+#: builtin/for-each-ref.c:1077
msgid "key"
msgstr ""
-#: builtin/for-each-ref.c:1090
+#: builtin/for-each-ref.c:1078
msgid "field name to sort on"
msgstr ""
@@ -5020,55 +5070,55 @@ msgstr ""
msgid "Checking connectivity"
msgstr ""
-#: builtin/fsck.c:544
+#: builtin/fsck.c:538
msgid "Checking object directories"
msgstr ""
-#: builtin/fsck.c:607
+#: builtin/fsck.c:601
msgid "git fsck [options] [<object>...]"
msgstr ""
-#: builtin/fsck.c:613
+#: builtin/fsck.c:607
msgid "show unreachable objects"
msgstr ""
-#: builtin/fsck.c:614
+#: builtin/fsck.c:608
msgid "show dangling objects"
msgstr ""
-#: builtin/fsck.c:615
+#: builtin/fsck.c:609
msgid "report tags"
msgstr ""
-#: builtin/fsck.c:616
+#: builtin/fsck.c:610
msgid "report root nodes"
msgstr ""
-#: builtin/fsck.c:617
+#: builtin/fsck.c:611
msgid "make index objects head nodes"
msgstr ""
-#: builtin/fsck.c:618
+#: builtin/fsck.c:612
msgid "make reflogs head nodes (default)"
msgstr ""
-#: builtin/fsck.c:619
+#: builtin/fsck.c:613
msgid "also consider packs and alternate objects"
msgstr ""
-#: builtin/fsck.c:620
+#: builtin/fsck.c:614
msgid "enable more strict checking"
msgstr ""
-#: builtin/fsck.c:622
+#: builtin/fsck.c:616
msgid "write dangling objects in .git/lost-found"
msgstr ""
-#: builtin/fsck.c:623 builtin/prune.c:144
+#: builtin/fsck.c:617 builtin/prune.c:144
msgid "show progress"
msgstr ""
-#: builtin/fsck.c:673
+#: builtin/fsck.c:667
msgid "Checking objects"
msgstr ""
@@ -5076,54 +5126,54 @@ msgstr ""
msgid "git gc [options]"
msgstr ""
-#: builtin/gc.c:90
+#: builtin/gc.c:91
#, c-format
msgid "Invalid %s: '%s'"
msgstr ""
-#: builtin/gc.c:117
+#: builtin/gc.c:118
#, c-format
msgid "insanely long object directory %.*s"
msgstr ""
-#: builtin/gc.c:273
+#: builtin/gc.c:287
msgid "prune unreferenced objects"
msgstr ""
-#: builtin/gc.c:275
+#: builtin/gc.c:289
msgid "be more thorough (increased runtime)"
msgstr ""
-#: builtin/gc.c:276
+#: builtin/gc.c:290
msgid "enable auto-gc mode"
msgstr ""
-#: builtin/gc.c:277
+#: builtin/gc.c:291
msgid "force running gc even if there may be another gc running"
msgstr ""
-#: builtin/gc.c:318
+#: builtin/gc.c:332
#, c-format
msgid "Auto packing the repository in background for optimum performance.\n"
msgstr ""
-#: builtin/gc.c:320
+#: builtin/gc.c:334
#, c-format
msgid "Auto packing the repository for optimum performance.\n"
msgstr ""
-#: builtin/gc.c:321
+#: builtin/gc.c:335
#, c-format
msgid "See \"git help gc\" for manual housekeeping.\n"
msgstr ""
-#: builtin/gc.c:336
+#: builtin/gc.c:353
#, c-format
msgid ""
"gc is already running on machine '%s' pid %<PRIuMAX> (use --force if not)"
msgstr ""
-#: builtin/gc.c:361
+#: builtin/gc.c:375
msgid ""
"There are too many unreachable loose objects; run 'git prune' to remove them."
msgstr ""
@@ -5137,224 +5187,219 @@ msgstr ""
msgid "grep: failed to create thread: %s"
msgstr ""
-#: builtin/grep.c:365
-#, c-format
-msgid "Failed to chdir: %s"
-msgstr ""
-
-#: builtin/grep.c:443 builtin/grep.c:478
+#: builtin/grep.c:441 builtin/grep.c:476
#, c-format
msgid "unable to read tree (%s)"
msgstr ""
-#: builtin/grep.c:493
+#: builtin/grep.c:491
#, c-format
msgid "unable to grep from object of type %s"
msgstr ""
-#: builtin/grep.c:549
+#: builtin/grep.c:547
#, c-format
msgid "switch `%c' expects a numerical value"
msgstr ""
-#: builtin/grep.c:566
+#: builtin/grep.c:564
#, c-format
msgid "cannot open '%s'"
msgstr ""
-#: builtin/grep.c:640
+#: builtin/grep.c:638
msgid "search in index instead of in the work tree"
msgstr ""
-#: builtin/grep.c:642
+#: builtin/grep.c:640
msgid "find in contents not managed by git"
msgstr ""
-#: builtin/grep.c:644
+#: builtin/grep.c:642
msgid "search in both tracked and untracked files"
msgstr ""
-#: builtin/grep.c:646
+#: builtin/grep.c:644
msgid "search also in ignored files"
msgstr ""
-#: builtin/grep.c:649
+#: builtin/grep.c:647
msgid "show non-matching lines"
msgstr ""
-#: builtin/grep.c:651
+#: builtin/grep.c:649
msgid "case insensitive matching"
msgstr ""
-#: builtin/grep.c:653
+#: builtin/grep.c:651
msgid "match patterns only at word boundaries"
msgstr ""
-#: builtin/grep.c:655
+#: builtin/grep.c:653
msgid "process binary files as text"
msgstr ""
-#: builtin/grep.c:657
+#: builtin/grep.c:655
msgid "don't match patterns in binary files"
msgstr ""
-#: builtin/grep.c:660
+#: builtin/grep.c:658
msgid "process binary files with textconv filters"
msgstr ""
-#: builtin/grep.c:662
+#: builtin/grep.c:660
msgid "descend at most <depth> levels"
msgstr ""
-#: builtin/grep.c:666
+#: builtin/grep.c:664
msgid "use extended POSIX regular expressions"
msgstr ""
-#: builtin/grep.c:669
+#: builtin/grep.c:667
msgid "use basic POSIX regular expressions (default)"
msgstr ""
-#: builtin/grep.c:672
+#: builtin/grep.c:670
msgid "interpret patterns as fixed strings"
msgstr ""
-#: builtin/grep.c:675
+#: builtin/grep.c:673
msgid "use Perl-compatible regular expressions"
msgstr ""
-#: builtin/grep.c:678
+#: builtin/grep.c:676
msgid "show line numbers"
msgstr ""
-#: builtin/grep.c:679
+#: builtin/grep.c:677
msgid "don't show filenames"
msgstr ""
-#: builtin/grep.c:680
+#: builtin/grep.c:678
msgid "show filenames"
msgstr ""
-#: builtin/grep.c:682
+#: builtin/grep.c:680
msgid "show filenames relative to top directory"
msgstr ""
-#: builtin/grep.c:684
+#: builtin/grep.c:682
msgid "show only filenames instead of matching lines"
msgstr ""
-#: builtin/grep.c:686
+#: builtin/grep.c:684
msgid "synonym for --files-with-matches"
msgstr ""
-#: builtin/grep.c:689
+#: builtin/grep.c:687
msgid "show only the names of files without match"
msgstr ""
-#: builtin/grep.c:691
+#: builtin/grep.c:689
msgid "print NUL after filenames"
msgstr ""
-#: builtin/grep.c:693
+#: builtin/grep.c:691
msgid "show the number of matches instead of matching lines"
msgstr ""
-#: builtin/grep.c:694
+#: builtin/grep.c:692
msgid "highlight matches"
msgstr ""
-#: builtin/grep.c:696
+#: builtin/grep.c:694
msgid "print empty line between matches from different files"
msgstr ""
-#: builtin/grep.c:698
+#: builtin/grep.c:696
msgid "show filename only once above matches from same file"
msgstr ""
-#: builtin/grep.c:701
+#: builtin/grep.c:699
msgid "show <n> context lines before and after matches"
msgstr ""
-#: builtin/grep.c:704
+#: builtin/grep.c:702
msgid "show <n> context lines before matches"
msgstr ""
-#: builtin/grep.c:706
+#: builtin/grep.c:704
msgid "show <n> context lines after matches"
msgstr ""
-#: builtin/grep.c:707
+#: builtin/grep.c:705
msgid "shortcut for -C NUM"
msgstr ""
-#: builtin/grep.c:710
+#: builtin/grep.c:708
msgid "show a line with the function name before matches"
msgstr ""
-#: builtin/grep.c:712
+#: builtin/grep.c:710
msgid "show the surrounding function"
msgstr ""
-#: builtin/grep.c:715
+#: builtin/grep.c:713
msgid "read patterns from file"
msgstr ""
-#: builtin/grep.c:717
+#: builtin/grep.c:715
msgid "match <pattern>"
msgstr ""
-#: builtin/grep.c:719
+#: builtin/grep.c:717
msgid "combine patterns specified with -e"
msgstr ""
-#: builtin/grep.c:731
+#: builtin/grep.c:729
msgid "indicate hit with exit status without output"
msgstr ""
-#: builtin/grep.c:733
+#: builtin/grep.c:731
msgid "show only matches from files that match all patterns"
msgstr ""
-#: builtin/grep.c:735
+#: builtin/grep.c:733
msgid "show parse tree for grep expression"
msgstr ""
-#: builtin/grep.c:739
+#: builtin/grep.c:737
msgid "pager"
msgstr ""
-#: builtin/grep.c:739
+#: builtin/grep.c:737
msgid "show matching files in the pager"
msgstr ""
-#: builtin/grep.c:742
+#: builtin/grep.c:740
msgid "allow calling of grep(1) (ignored by this build)"
msgstr ""
-#: builtin/grep.c:743 builtin/show-ref.c:187
+#: builtin/grep.c:741 builtin/show-ref.c:187
msgid "show usage"
msgstr ""
-#: builtin/grep.c:810
+#: builtin/grep.c:808
msgid "no pattern given."
msgstr ""
-#: builtin/grep.c:868
+#: builtin/grep.c:866
msgid "--open-files-in-pager only works on the worktree"
msgstr ""
-#: builtin/grep.c:891
+#: builtin/grep.c:892
msgid "--cached or --untracked cannot be used with --no-index."
msgstr ""
-#: builtin/grep.c:896
+#: builtin/grep.c:897
msgid "--no-index or --untracked cannot be used with revs."
msgstr ""
-#: builtin/grep.c:899
+#: builtin/grep.c:900
msgid "--[no-]exclude-standard cannot be used for tracked contents."
msgstr ""
-#: builtin/grep.c:907
+#: builtin/grep.c:908
msgid "both --cached and trees are given."
msgstr ""
@@ -5368,7 +5413,7 @@ msgstr ""
msgid "git hash-object --stdin-paths < <list-of-paths>"
msgstr ""
-#: builtin/hash-object.c:72 builtin/tag.c:521
+#: builtin/hash-object.c:72 builtin/tag.c:609
msgid "type"
msgstr ""
@@ -5508,280 +5553,291 @@ msgstr ""
msgid "`git %s' is aliased to `%s'"
msgstr ""
-#: builtin/index-pack.c:184
+#: builtin/index-pack.c:145
+#, c-format
+msgid "unable to open %s"
+msgstr ""
+
+#: builtin/index-pack.c:191
#, c-format
msgid "object type mismatch at %s"
msgstr ""
-#: builtin/index-pack.c:204
-msgid "object of unexpected type"
+#: builtin/index-pack.c:211
+#, c-format
+msgid "did not receive expected object %s"
+msgstr ""
+
+#: builtin/index-pack.c:214
+#, c-format
+msgid "object %s: expected type %s, found %s"
msgstr ""
-#: builtin/index-pack.c:244
+#: builtin/index-pack.c:256
#, c-format
msgid "cannot fill %d byte"
msgid_plural "cannot fill %d bytes"
msgstr[0] ""
msgstr[1] ""
-#: builtin/index-pack.c:254
+#: builtin/index-pack.c:266
msgid "early EOF"
msgstr ""
-#: builtin/index-pack.c:255
+#: builtin/index-pack.c:267
msgid "read error on input"
msgstr ""
-#: builtin/index-pack.c:267
+#: builtin/index-pack.c:279
msgid "used more bytes than were available"
msgstr ""
-#: builtin/index-pack.c:274
+#: builtin/index-pack.c:286
msgid "pack too large for current definition of off_t"
msgstr ""
-#: builtin/index-pack.c:290
+#: builtin/index-pack.c:302
#, c-format
msgid "unable to create '%s'"
msgstr ""
-#: builtin/index-pack.c:295
+#: builtin/index-pack.c:307
#, c-format
msgid "cannot open packfile '%s'"
msgstr ""
-#: builtin/index-pack.c:309
+#: builtin/index-pack.c:321
msgid "pack signature mismatch"
msgstr ""
-#: builtin/index-pack.c:311
+#: builtin/index-pack.c:323
#, c-format
msgid "pack version %<PRIu32> unsupported"
msgstr ""
-#: builtin/index-pack.c:329
+#: builtin/index-pack.c:341
#, c-format
msgid "pack has bad object at offset %lu: %s"
msgstr ""
-#: builtin/index-pack.c:451
+#: builtin/index-pack.c:462
#, c-format
msgid "inflate returned %d"
msgstr ""
-#: builtin/index-pack.c:500
+#: builtin/index-pack.c:511
msgid "offset value overflow for delta base object"
msgstr ""
-#: builtin/index-pack.c:508
+#: builtin/index-pack.c:519
msgid "delta base offset is out of bound"
msgstr ""
-#: builtin/index-pack.c:516
+#: builtin/index-pack.c:527
#, c-format
msgid "unknown object type %d"
msgstr ""
-#: builtin/index-pack.c:547
+#: builtin/index-pack.c:558
msgid "cannot pread pack file"
msgstr ""
-#: builtin/index-pack.c:549
+#: builtin/index-pack.c:560
#, c-format
msgid "premature end of pack file, %lu byte missing"
msgid_plural "premature end of pack file, %lu bytes missing"
msgstr[0] ""
msgstr[1] ""
-#: builtin/index-pack.c:575
+#: builtin/index-pack.c:586
msgid "serious inflate inconsistency"
msgstr ""
-#: builtin/index-pack.c:666 builtin/index-pack.c:672 builtin/index-pack.c:695
-#: builtin/index-pack.c:729 builtin/index-pack.c:738
+#: builtin/index-pack.c:677 builtin/index-pack.c:683 builtin/index-pack.c:706
+#: builtin/index-pack.c:740 builtin/index-pack.c:749
#, c-format
msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr ""
-#: builtin/index-pack.c:669 builtin/pack-objects.c:162
+#: builtin/index-pack.c:680 builtin/pack-objects.c:162
#: builtin/pack-objects.c:254
#, c-format
msgid "unable to read %s"
msgstr ""
-#: builtin/index-pack.c:735
+#: builtin/index-pack.c:746
#, c-format
msgid "cannot read existing object %s"
msgstr ""
-#: builtin/index-pack.c:749
+#: builtin/index-pack.c:760
#, c-format
msgid "invalid blob object %s"
msgstr ""
-#: builtin/index-pack.c:763
+#: builtin/index-pack.c:774
#, c-format
msgid "invalid %s"
msgstr ""
-#: builtin/index-pack.c:766
+#: builtin/index-pack.c:777
msgid "Error in object"
msgstr ""
-#: builtin/index-pack.c:768
+#: builtin/index-pack.c:779
#, c-format
msgid "Not all child objects of %s are reachable"
msgstr ""
-#: builtin/index-pack.c:839 builtin/index-pack.c:869
+#: builtin/index-pack.c:851 builtin/index-pack.c:881
msgid "failed to apply delta"
msgstr ""
-#: builtin/index-pack.c:1010
+#: builtin/index-pack.c:1022
msgid "Receiving objects"
msgstr ""
-#: builtin/index-pack.c:1010
+#: builtin/index-pack.c:1022
msgid "Indexing objects"
msgstr ""
-#: builtin/index-pack.c:1036
+#: builtin/index-pack.c:1048
msgid "pack is corrupted (SHA1 mismatch)"
msgstr ""
-#: builtin/index-pack.c:1041
+#: builtin/index-pack.c:1053
msgid "cannot fstat packfile"
msgstr ""
-#: builtin/index-pack.c:1044
+#: builtin/index-pack.c:1056
msgid "pack has junk at the end"
msgstr ""
-#: builtin/index-pack.c:1055
+#: builtin/index-pack.c:1067
msgid "confusion beyond insanity in parse_pack_objects()"
msgstr ""
-#: builtin/index-pack.c:1078
+#: builtin/index-pack.c:1090
msgid "Resolving deltas"
msgstr ""
-#: builtin/index-pack.c:1088
+#: builtin/index-pack.c:1100
#, c-format
msgid "unable to create thread: %s"
msgstr ""
-#: builtin/index-pack.c:1130
+#: builtin/index-pack.c:1142
msgid "confusion beyond insanity"
msgstr ""
-#: builtin/index-pack.c:1138
+#: builtin/index-pack.c:1150
#, c-format
msgid "completed with %d local objects"
msgstr ""
-#: builtin/index-pack.c:1148
+#: builtin/index-pack.c:1160
#, c-format
msgid "Unexpected tail checksum for %s (disk corruption?)"
msgstr ""
-#: builtin/index-pack.c:1152
+#: builtin/index-pack.c:1164
#, c-format
msgid "pack has %d unresolved delta"
msgid_plural "pack has %d unresolved deltas"
msgstr[0] ""
msgstr[1] ""
-#: builtin/index-pack.c:1177
+#: builtin/index-pack.c:1189
#, c-format
msgid "unable to deflate appended object (%d)"
msgstr ""
-#: builtin/index-pack.c:1256
+#: builtin/index-pack.c:1268
#, c-format
msgid "local object %s is corrupt"
msgstr ""
-#: builtin/index-pack.c:1280
+#: builtin/index-pack.c:1292
msgid "error while closing pack file"
msgstr ""
-#: builtin/index-pack.c:1293
+#: builtin/index-pack.c:1305
#, c-format
msgid "cannot write keep file '%s'"
msgstr ""
-#: builtin/index-pack.c:1301
+#: builtin/index-pack.c:1313
#, c-format
msgid "cannot close written keep file '%s'"
msgstr ""
-#: builtin/index-pack.c:1314
+#: builtin/index-pack.c:1326
msgid "cannot store pack file"
msgstr ""
-#: builtin/index-pack.c:1325
+#: builtin/index-pack.c:1337
msgid "cannot store index file"
msgstr ""
-#: builtin/index-pack.c:1358
+#: builtin/index-pack.c:1370
#, c-format
msgid "bad pack.indexversion=%<PRIu32>"
msgstr ""
-#: builtin/index-pack.c:1364
+#: builtin/index-pack.c:1376
#, c-format
msgid "invalid number of threads specified (%d)"
msgstr ""
-#: builtin/index-pack.c:1368 builtin/index-pack.c:1546
+#: builtin/index-pack.c:1380 builtin/index-pack.c:1559
#, c-format
msgid "no threads support, ignoring %s"
msgstr ""
-#: builtin/index-pack.c:1426
+#: builtin/index-pack.c:1438
#, c-format
msgid "Cannot open existing pack file '%s'"
msgstr ""
-#: builtin/index-pack.c:1428
+#: builtin/index-pack.c:1440
#, c-format
msgid "Cannot open existing pack idx file for '%s'"
msgstr ""
-#: builtin/index-pack.c:1475
+#: builtin/index-pack.c:1487
#, c-format
msgid "non delta: %d object"
msgid_plural "non delta: %d objects"
msgstr[0] ""
msgstr[1] ""
-#: builtin/index-pack.c:1482
+#: builtin/index-pack.c:1494
#, c-format
msgid "chain length = %d: %lu object"
msgid_plural "chain length = %d: %lu objects"
msgstr[0] ""
msgstr[1] ""
-#: builtin/index-pack.c:1510
+#: builtin/index-pack.c:1523
msgid "Cannot come back to cwd"
msgstr ""
-#: builtin/index-pack.c:1558 builtin/index-pack.c:1561
-#: builtin/index-pack.c:1573 builtin/index-pack.c:1577
+#: builtin/index-pack.c:1571 builtin/index-pack.c:1574
+#: builtin/index-pack.c:1586 builtin/index-pack.c:1590
#, c-format
msgid "bad %s"
msgstr ""
-#: builtin/index-pack.c:1591
+#: builtin/index-pack.c:1604
msgid "--fix-thin cannot be used without --stdin"
msgstr ""
-#: builtin/index-pack.c:1595 builtin/index-pack.c:1605
+#: builtin/index-pack.c:1608 builtin/index-pack.c:1617
#, c-format
msgid "packfile name '%s' does not end with '.pack'"
msgstr ""
-#: builtin/index-pack.c:1614
+#: builtin/index-pack.c:1625
msgid "--verify with no packfile name given"
msgstr ""
@@ -5908,7 +5964,7 @@ msgstr ""
msgid "specify that the git repository is to be shared amongst several users"
msgstr ""
-#: builtin/init-db.c:491 builtin/prune-packed.c:79 builtin/repack.c:168
+#: builtin/init-db.c:491 builtin/prune-packed.c:79 builtin/repack.c:172
msgid "be quiet"
msgstr ""
@@ -5946,38 +6002,38 @@ msgstr ""
msgid " or: git show [options] <object>..."
msgstr ""
-#: builtin/log.c:125
+#: builtin/log.c:127
msgid "suppress diff output"
msgstr ""
-#: builtin/log.c:126
+#: builtin/log.c:128
msgid "show source"
msgstr ""
-#: builtin/log.c:127
+#: builtin/log.c:129
msgid "Use mail map file"
msgstr ""
-#: builtin/log.c:128
+#: builtin/log.c:130
msgid "decorate options"
msgstr ""
-#: builtin/log.c:231
+#: builtin/log.c:229
#, c-format
msgid "Final output: %d %s\n"
msgstr ""
-#: builtin/log.c:473 builtin/log.c:565
+#: builtin/log.c:470 builtin/log.c:562
#, c-format
msgid "Could not read object %s"
msgstr ""
-#: builtin/log.c:589
+#: builtin/log.c:586
#, c-format
msgid "Unknown type: %d"
msgstr ""
-#: builtin/log.c:689
+#: builtin/log.c:687
msgid "format.headers without value"
msgstr ""
@@ -5998,200 +6054,209 @@ msgstr ""
msgid "Not a range."
msgstr ""
-#: builtin/log.c:911
+#: builtin/log.c:916
msgid "Cover letter needs email format"
msgstr ""
-#: builtin/log.c:987
+#: builtin/log.c:995
#, c-format
msgid "insane in-reply-to: %s"
msgstr ""
-#: builtin/log.c:1015
+#: builtin/log.c:1023
msgid "git format-patch [options] [<since> | <revision range>]"
msgstr ""
-#: builtin/log.c:1060
+#: builtin/log.c:1068
msgid "Two output directories?"
msgstr ""
-#: builtin/log.c:1175
+#: builtin/log.c:1183
msgid "use [PATCH n/m] even with a single patch"
msgstr ""
-#: builtin/log.c:1178
+#: builtin/log.c:1186
msgid "use [PATCH] even with multiple patches"
msgstr ""
-#: builtin/log.c:1182
+#: builtin/log.c:1190
msgid "print patches to standard out"
msgstr ""
-#: builtin/log.c:1184
+#: builtin/log.c:1192
msgid "generate a cover letter"
msgstr ""
-#: builtin/log.c:1186
+#: builtin/log.c:1194
msgid "use simple number sequence for output file names"
msgstr ""
-#: builtin/log.c:1187
+#: builtin/log.c:1195
msgid "sfx"
msgstr ""
-#: builtin/log.c:1188
+#: builtin/log.c:1196
msgid "use <sfx> instead of '.patch'"
msgstr ""
-#: builtin/log.c:1190
+#: builtin/log.c:1198
msgid "start numbering patches at <n> instead of 1"
msgstr ""
-#: builtin/log.c:1192
+#: builtin/log.c:1200
msgid "mark the series as Nth re-roll"
msgstr ""
-#: builtin/log.c:1194
+#: builtin/log.c:1202
msgid "Use [<prefix>] instead of [PATCH]"
msgstr ""
-#: builtin/log.c:1197
+#: builtin/log.c:1205
msgid "store resulting files in <dir>"
msgstr ""
-#: builtin/log.c:1200
+#: builtin/log.c:1208
msgid "don't strip/add [PATCH]"
msgstr ""
-#: builtin/log.c:1203
+#: builtin/log.c:1211
msgid "don't output binary diffs"
msgstr ""
-#: builtin/log.c:1205
+#: builtin/log.c:1213
msgid "don't include a patch matching a commit upstream"
msgstr ""
-#: builtin/log.c:1207
+#: builtin/log.c:1215
msgid "show patch format instead of default (patch + stat)"
msgstr ""
-#: builtin/log.c:1209
+#: builtin/log.c:1217
msgid "Messaging"
msgstr ""
-#: builtin/log.c:1210
+#: builtin/log.c:1218
msgid "header"
msgstr ""
-#: builtin/log.c:1211
+#: builtin/log.c:1219
msgid "add email header"
msgstr ""
-#: builtin/log.c:1212 builtin/log.c:1214
+#: builtin/log.c:1220 builtin/log.c:1222
msgid "email"
msgstr ""
-#: builtin/log.c:1212
+#: builtin/log.c:1220
msgid "add To: header"
msgstr ""
-#: builtin/log.c:1214
+#: builtin/log.c:1222
msgid "add Cc: header"
msgstr ""
-#: builtin/log.c:1216
+#: builtin/log.c:1224
msgid "ident"
msgstr ""
-#: builtin/log.c:1217
+#: builtin/log.c:1225
msgid "set From address to <ident> (or committer ident if absent)"
msgstr ""
-#: builtin/log.c:1219
+#: builtin/log.c:1227
msgid "message-id"
msgstr ""
-#: builtin/log.c:1220
+#: builtin/log.c:1228
msgid "make first mail a reply to <message-id>"
msgstr ""
-#: builtin/log.c:1221 builtin/log.c:1224
+#: builtin/log.c:1229 builtin/log.c:1232
msgid "boundary"
msgstr ""
-#: builtin/log.c:1222
+#: builtin/log.c:1230
msgid "attach the patch"
msgstr ""
-#: builtin/log.c:1225
+#: builtin/log.c:1233
msgid "inline the patch"
msgstr ""
-#: builtin/log.c:1229
+#: builtin/log.c:1237
msgid "enable message threading, styles: shallow, deep"
msgstr ""
-#: builtin/log.c:1231
+#: builtin/log.c:1239
msgid "signature"
msgstr ""
-#: builtin/log.c:1232
+#: builtin/log.c:1240
msgid "add a signature"
msgstr ""
-#: builtin/log.c:1233
+#: builtin/log.c:1242
+msgid "add a signature from a file"
+msgstr ""
+
+#: builtin/log.c:1243
msgid "don't print the patch filenames"
msgstr ""
-#: builtin/log.c:1307
+#: builtin/log.c:1317
#, c-format
msgid "invalid ident line: %s"
msgstr ""
-#: builtin/log.c:1322
+#: builtin/log.c:1332
msgid "-n and -k are mutually exclusive."
msgstr ""
-#: builtin/log.c:1324
+#: builtin/log.c:1334
msgid "--subject-prefix and -k are mutually exclusive."
msgstr ""
-#: builtin/log.c:1332
+#: builtin/log.c:1342
msgid "--name-only does not make sense"
msgstr ""
-#: builtin/log.c:1334
+#: builtin/log.c:1344
msgid "--name-status does not make sense"
msgstr ""
-#: builtin/log.c:1336
+#: builtin/log.c:1346
msgid "--check does not make sense"
msgstr ""
-#: builtin/log.c:1359
+#: builtin/log.c:1369
msgid "standard output, or directory, which one?"
msgstr ""
-#: builtin/log.c:1361
+#: builtin/log.c:1371
#, c-format
msgid "Could not create directory '%s'"
msgstr ""
-#: builtin/log.c:1509
+#: builtin/log.c:1468
+#, c-format
+msgid "unable to read signature file '%s'"
+msgstr ""
+
+#: builtin/log.c:1531
msgid "Failed to create output files"
msgstr ""
-#: builtin/log.c:1558
+#: builtin/log.c:1579
msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]"
msgstr ""
-#: builtin/log.c:1613
+#: builtin/log.c:1634
#, c-format
msgid ""
"Could not find a tracked remote branch, please specify <upstream> manually.\n"
msgstr ""
-#: builtin/log.c:1626 builtin/log.c:1628 builtin/log.c:1640
+#: builtin/log.c:1647 builtin/log.c:1649 builtin/log.c:1661
#, c-format
msgid "Unknown commit %s"
msgstr ""
@@ -6472,31 +6537,31 @@ msgstr ""
msgid "git write-tree failed to write a tree"
msgstr ""
-#: builtin/merge.c:681
+#: builtin/merge.c:678
msgid "Not handling anything other than two heads merge."
msgstr ""
-#: builtin/merge.c:695
+#: builtin/merge.c:692
#, c-format
msgid "Unknown option for merge-recursive: -X%s"
msgstr ""
-#: builtin/merge.c:709
+#: builtin/merge.c:705
#, c-format
msgid "unable to write %s"
msgstr ""
-#: builtin/merge.c:798
+#: builtin/merge.c:794
#, c-format
msgid "Could not read from '%s'"
msgstr ""
-#: builtin/merge.c:807
+#: builtin/merge.c:803
#, c-format
msgid "Not committing merge; use 'git commit' to complete the merge.\n"
msgstr ""
-#: builtin/merge.c:813
+#: builtin/merge.c:809
#, c-format
msgid ""
"Please enter a commit message to explain why this merge is necessary,\n"
@@ -6506,156 +6571,156 @@ msgid ""
"the commit.\n"
msgstr ""
-#: builtin/merge.c:837
+#: builtin/merge.c:833
msgid "Empty commit message."
msgstr ""
-#: builtin/merge.c:849
+#: builtin/merge.c:845
#, c-format
msgid "Wonderful.\n"
msgstr ""
-#: builtin/merge.c:914
+#: builtin/merge.c:908
#, c-format
msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
msgstr ""
-#: builtin/merge.c:930
+#: builtin/merge.c:924
#, c-format
msgid "'%s' is not a commit"
msgstr ""
-#: builtin/merge.c:971
+#: builtin/merge.c:965
msgid "No current branch."
msgstr ""
-#: builtin/merge.c:973
+#: builtin/merge.c:967
msgid "No remote for the current branch."
msgstr ""
-#: builtin/merge.c:975
+#: builtin/merge.c:969
msgid "No default upstream defined for the current branch."
msgstr ""
-#: builtin/merge.c:980
+#: builtin/merge.c:974
#, c-format
msgid "No remote-tracking branch for %s from %s"
msgstr ""
-#: builtin/merge.c:1136
+#: builtin/merge.c:1130
msgid "There is no merge to abort (MERGE_HEAD missing)."
msgstr ""
-#: builtin/merge.c:1152 git-pull.sh:31
+#: builtin/merge.c:1146 git-pull.sh:31
msgid ""
"You have not concluded your merge (MERGE_HEAD exists).\n"
"Please, commit your changes before you can merge."
msgstr ""
-#: builtin/merge.c:1155 git-pull.sh:34
+#: builtin/merge.c:1149 git-pull.sh:34
msgid "You have not concluded your merge (MERGE_HEAD exists)."
msgstr ""
-#: builtin/merge.c:1159
+#: builtin/merge.c:1153
msgid ""
"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
"Please, commit your changes before you can merge."
msgstr ""
-#: builtin/merge.c:1162
+#: builtin/merge.c:1156
msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
msgstr ""
-#: builtin/merge.c:1171
+#: builtin/merge.c:1165
msgid "You cannot combine --squash with --no-ff."
msgstr ""
-#: builtin/merge.c:1180
+#: builtin/merge.c:1174
msgid "No commit specified and merge.defaultToUpstream not set."
msgstr ""
-#: builtin/merge.c:1212
+#: builtin/merge.c:1206
msgid "Can merge only exactly one commit into empty head"
msgstr ""
-#: builtin/merge.c:1215
+#: builtin/merge.c:1209
msgid "Squash commit into empty head not supported yet"
msgstr ""
-#: builtin/merge.c:1217
+#: builtin/merge.c:1211
msgid "Non-fast-forward commit does not make sense into an empty head"
msgstr ""
-#: builtin/merge.c:1222
+#: builtin/merge.c:1216
#, c-format
msgid "%s - not something we can merge"
msgstr ""
-#: builtin/merge.c:1273
+#: builtin/merge.c:1267
#, c-format
msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
msgstr ""
-#: builtin/merge.c:1276
+#: builtin/merge.c:1270
#, c-format
msgid "Commit %s has a bad GPG signature allegedly by %s."
msgstr ""
-#: builtin/merge.c:1279
+#: builtin/merge.c:1273
#, c-format
msgid "Commit %s does not have a GPG signature."
msgstr ""
-#: builtin/merge.c:1282
+#: builtin/merge.c:1276
#, c-format
msgid "Commit %s has a good GPG signature by %s\n"
msgstr ""
-#: builtin/merge.c:1366
+#: builtin/merge.c:1357
#, c-format
msgid "Updating %s..%s\n"
msgstr ""
-#: builtin/merge.c:1405
+#: builtin/merge.c:1396
#, c-format
msgid "Trying really trivial in-index merge...\n"
msgstr ""
-#: builtin/merge.c:1412
+#: builtin/merge.c:1403
#, c-format
msgid "Nope.\n"
msgstr ""
-#: builtin/merge.c:1444
+#: builtin/merge.c:1435
msgid "Not possible to fast-forward, aborting."
msgstr ""
-#: builtin/merge.c:1467 builtin/merge.c:1546
+#: builtin/merge.c:1458 builtin/merge.c:1537
#, c-format
msgid "Rewinding the tree to pristine...\n"
msgstr ""
-#: builtin/merge.c:1471
+#: builtin/merge.c:1462
#, c-format
msgid "Trying merge strategy %s...\n"
msgstr ""
-#: builtin/merge.c:1537
+#: builtin/merge.c:1528
#, c-format
msgid "No merge strategy handled the merge.\n"
msgstr ""
-#: builtin/merge.c:1539
+#: builtin/merge.c:1530
#, c-format
msgid "Merge with strategy %s failed.\n"
msgstr ""
-#: builtin/merge.c:1548
+#: builtin/merge.c:1539
#, c-format
msgid "Using the %s to prepare resolving by hand.\n"
msgstr ""
-#: builtin/merge.c:1560
+#: builtin/merge.c:1551
#, c-format
msgid "Automatic merge went well; stopped before committing as requested\n"
msgstr ""
@@ -6805,79 +6870,79 @@ msgstr ""
msgid "not under version control"
msgstr ""
-#: builtin/mv.c:207
+#: builtin/mv.c:208
msgid "destination exists"
msgstr ""
-#: builtin/mv.c:215
+#: builtin/mv.c:216
#, c-format
msgid "overwriting '%s'"
msgstr ""
-#: builtin/mv.c:218
+#: builtin/mv.c:219
msgid "Cannot overwrite"
msgstr ""
-#: builtin/mv.c:221
+#: builtin/mv.c:222
msgid "multiple sources for the same target"
msgstr ""
-#: builtin/mv.c:223
+#: builtin/mv.c:224
msgid "destination directory does not exist"
msgstr ""
-#: builtin/mv.c:243
+#: builtin/mv.c:244
#, c-format
msgid "%s, source=%s, destination=%s"
msgstr ""
-#: builtin/mv.c:253
+#: builtin/mv.c:254
#, c-format
msgid "Renaming %s to %s\n"
msgstr ""
-#: builtin/mv.c:256 builtin/remote.c:726 builtin/repack.c:355
+#: builtin/mv.c:257 builtin/remote.c:725 builtin/repack.c:358
#, c-format
msgid "renaming '%s' failed"
msgstr ""
-#: builtin/name-rev.c:258
+#: builtin/name-rev.c:255
msgid "git name-rev [options] <commit>..."
msgstr ""
-#: builtin/name-rev.c:259
+#: builtin/name-rev.c:256
msgid "git name-rev [options] --all"
msgstr ""
-#: builtin/name-rev.c:260
+#: builtin/name-rev.c:257
msgid "git name-rev [options] --stdin"
msgstr ""
-#: builtin/name-rev.c:312
+#: builtin/name-rev.c:309
msgid "print only names (no SHA-1)"
msgstr ""
-#: builtin/name-rev.c:313
+#: builtin/name-rev.c:310
msgid "only use tags to name the commits"
msgstr ""
-#: builtin/name-rev.c:315
+#: builtin/name-rev.c:312
msgid "only use refs matching <pattern>"
msgstr ""
-#: builtin/name-rev.c:317
+#: builtin/name-rev.c:314
msgid "list all commits reachable from all refs"
msgstr ""
-#: builtin/name-rev.c:318
+#: builtin/name-rev.c:315
msgid "read from stdin"
msgstr ""
-#: builtin/name-rev.c:319
+#: builtin/name-rev.c:316
msgid "allow to print `undefined` names (default)"
msgstr ""
-#: builtin/name-rev.c:325
+#: builtin/name-rev.c:322
msgid "dereference tags in the input (internal use)"
msgstr ""
@@ -7000,7 +7065,7 @@ msgstr ""
msgid "failed to finish 'show' for object '%s'"
msgstr ""
-#: builtin/notes.c:167 builtin/tag.c:373
+#: builtin/notes.c:167 builtin/tag.c:477
#, c-format
msgid "could not create file '%s'"
msgstr ""
@@ -7023,12 +7088,12 @@ msgstr ""
msgid "The note contents has been left in %s"
msgstr ""
-#: builtin/notes.c:248 builtin/tag.c:604
+#: builtin/notes.c:248 builtin/tag.c:692
#, c-format
msgid "cannot read '%s'"
msgstr ""
-#: builtin/notes.c:250 builtin/tag.c:607
+#: builtin/notes.c:250 builtin/tag.c:695
#, c-format
msgid "could not open or read '%s'"
msgstr ""
@@ -7036,7 +7101,7 @@ msgstr ""
#: builtin/notes.c:269 builtin/notes.c:320 builtin/notes.c:322
#: builtin/notes.c:382 builtin/notes.c:436 builtin/notes.c:519
#: builtin/notes.c:524 builtin/notes.c:599 builtin/notes.c:641
-#: builtin/notes.c:843 builtin/tag.c:620
+#: builtin/notes.c:843 builtin/tag.c:708
#, c-format
msgid "Failed to resolve '%s' as a valid ref."
msgstr ""
@@ -7081,7 +7146,7 @@ msgid "note contents in a file"
msgstr ""
#: builtin/notes.c:415 builtin/notes.c:418 builtin/notes.c:572
-#: builtin/notes.c:575 builtin/tag.c:539
+#: builtin/notes.c:575 builtin/tag.c:627
msgid "object"
msgstr ""
@@ -7191,7 +7256,7 @@ msgstr ""
msgid "use notes from <notes_ref>"
msgstr ""
-#: builtin/notes.c:978 builtin/remote.c:1593
+#: builtin/notes.c:978 builtin/remote.c:1616
#, c-format
msgid "Unknown subcommand: %s"
msgstr ""
@@ -7221,155 +7286,155 @@ msgstr ""
msgid "Compressing objects"
msgstr ""
-#: builtin/pack-objects.c:2530
+#: builtin/pack-objects.c:2526
#, c-format
msgid "unsupported index version %s"
msgstr ""
-#: builtin/pack-objects.c:2534
+#: builtin/pack-objects.c:2530
#, c-format
msgid "bad index version '%s'"
msgstr ""
-#: builtin/pack-objects.c:2557
+#: builtin/pack-objects.c:2553
#, c-format
msgid "option %s does not accept negative form"
msgstr ""
-#: builtin/pack-objects.c:2561
+#: builtin/pack-objects.c:2557
#, c-format
msgid "unable to parse value '%s' for option %s"
msgstr ""
-#: builtin/pack-objects.c:2580
+#: builtin/pack-objects.c:2576
msgid "do not show progress meter"
msgstr ""
-#: builtin/pack-objects.c:2582
+#: builtin/pack-objects.c:2578
msgid "show progress meter"
msgstr ""
-#: builtin/pack-objects.c:2584
+#: builtin/pack-objects.c:2580
msgid "show progress meter during object writing phase"
msgstr ""
-#: builtin/pack-objects.c:2587
+#: builtin/pack-objects.c:2583
msgid "similar to --all-progress when progress meter is shown"
msgstr ""
-#: builtin/pack-objects.c:2588
+#: builtin/pack-objects.c:2584
msgid "version[,offset]"
msgstr ""
-#: builtin/pack-objects.c:2589
+#: builtin/pack-objects.c:2585
msgid "write the pack index file in the specified idx format version"
msgstr ""
-#: builtin/pack-objects.c:2592
+#: builtin/pack-objects.c:2588
msgid "maximum size of each output pack file"
msgstr ""
-#: builtin/pack-objects.c:2594
+#: builtin/pack-objects.c:2590
msgid "ignore borrowed objects from alternate object store"
msgstr ""
-#: builtin/pack-objects.c:2596
+#: builtin/pack-objects.c:2592
msgid "ignore packed objects"
msgstr ""
-#: builtin/pack-objects.c:2598
+#: builtin/pack-objects.c:2594
msgid "limit pack window by objects"
msgstr ""
-#: builtin/pack-objects.c:2600
+#: builtin/pack-objects.c:2596
msgid "limit pack window by memory in addition to object limit"
msgstr ""
-#: builtin/pack-objects.c:2602
+#: builtin/pack-objects.c:2598
msgid "maximum length of delta chain allowed in the resulting pack"
msgstr ""
-#: builtin/pack-objects.c:2604
+#: builtin/pack-objects.c:2600
msgid "reuse existing deltas"
msgstr ""
-#: builtin/pack-objects.c:2606
+#: builtin/pack-objects.c:2602
msgid "reuse existing objects"
msgstr ""
-#: builtin/pack-objects.c:2608
+#: builtin/pack-objects.c:2604
msgid "use OFS_DELTA objects"
msgstr ""
-#: builtin/pack-objects.c:2610
+#: builtin/pack-objects.c:2606
msgid "use threads when searching for best delta matches"
msgstr ""
-#: builtin/pack-objects.c:2612
+#: builtin/pack-objects.c:2608
msgid "do not create an empty pack output"
msgstr ""
-#: builtin/pack-objects.c:2614
+#: builtin/pack-objects.c:2610
msgid "read revision arguments from standard input"
msgstr ""
-#: builtin/pack-objects.c:2616
+#: builtin/pack-objects.c:2612
msgid "limit the objects to those that are not yet packed"
msgstr ""
-#: builtin/pack-objects.c:2619
+#: builtin/pack-objects.c:2615
msgid "include objects reachable from any reference"
msgstr ""
-#: builtin/pack-objects.c:2622
+#: builtin/pack-objects.c:2618
msgid "include objects referred by reflog entries"
msgstr ""
-#: builtin/pack-objects.c:2625
+#: builtin/pack-objects.c:2621
msgid "output pack to stdout"
msgstr ""
-#: builtin/pack-objects.c:2627
+#: builtin/pack-objects.c:2623
msgid "include tag objects that refer to objects to be packed"
msgstr ""
-#: builtin/pack-objects.c:2629
+#: builtin/pack-objects.c:2625
msgid "keep unreachable objects"
msgstr ""
-#: builtin/pack-objects.c:2630 parse-options.h:140
+#: builtin/pack-objects.c:2626 parse-options.h:140
msgid "time"
msgstr ""
-#: builtin/pack-objects.c:2631
+#: builtin/pack-objects.c:2627
msgid "unpack unreachable objects newer than <time>"
msgstr ""
-#: builtin/pack-objects.c:2634
+#: builtin/pack-objects.c:2630
msgid "create thin packs"
msgstr ""
-#: builtin/pack-objects.c:2636
+#: builtin/pack-objects.c:2632
msgid "ignore packs that have companion .keep file"
msgstr ""
-#: builtin/pack-objects.c:2638
+#: builtin/pack-objects.c:2634
msgid "pack compression level"
msgstr ""
-#: builtin/pack-objects.c:2640
+#: builtin/pack-objects.c:2636
msgid "do not hide commits by grafts"
msgstr ""
-#: builtin/pack-objects.c:2642
+#: builtin/pack-objects.c:2638
msgid "use a bitmap index if available to speed up counting objects"
msgstr ""
-#: builtin/pack-objects.c:2644
+#: builtin/pack-objects.c:2640
msgid "write a bitmap index together with the pack index"
msgstr ""
-#: builtin/pack-objects.c:2723
+#: builtin/pack-objects.c:2719
msgid "Counting objects"
msgstr ""
@@ -7421,13 +7486,13 @@ msgstr ""
msgid "--delete only accepts plain target ref names"
msgstr ""
-#: builtin/push.c:140
+#: builtin/push.c:139
msgid ""
"\n"
"To choose either option permanently, see push.default in 'git help config'."
msgstr ""
-#: builtin/push.c:143
+#: builtin/push.c:142
#, c-format
msgid ""
"The upstream branch of your current branch does not match\n"
@@ -7442,7 +7507,7 @@ msgid ""
"%s"
msgstr ""
-#: builtin/push.c:158
+#: builtin/push.c:157
#, c-format
msgid ""
"You are not currently on a branch.\n"
@@ -7452,7 +7517,7 @@ msgid ""
" git push %s HEAD:<name-of-remote-branch>\n"
msgstr ""
-#: builtin/push.c:172
+#: builtin/push.c:171
#, c-format
msgid ""
"The current branch %s has no upstream branch.\n"
@@ -7461,12 +7526,12 @@ msgid ""
" git push --set-upstream %s %s\n"
msgstr ""
-#: builtin/push.c:180
+#: builtin/push.c:179
#, c-format
msgid "The current branch %s has multiple upstream branches, refusing to push."
msgstr ""
-#: builtin/push.c:183
+#: builtin/push.c:182
#, c-format
msgid ""
"You are pushing to remote '%s', which is not the upstream of\n"
@@ -7474,7 +7539,7 @@ msgid ""
"to update which remote branch."
msgstr ""
-#: builtin/push.c:206
+#: builtin/push.c:205
msgid ""
"push.default is unset; its implicit value has changed in\n"
"Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -7499,12 +7564,12 @@ msgid ""
"'current' instead of 'simple' if you sometimes use older versions of Git)"
msgstr ""
-#: builtin/push.c:273
+#: builtin/push.c:272
msgid ""
"You didn't specify any refspecs to push, and push.default is \"nothing\"."
msgstr ""
-#: builtin/push.c:280
+#: builtin/push.c:279
msgid ""
"Updates were rejected because the tip of your current branch is behind\n"
"its remote counterpart. Integrate the remote changes (e.g.\n"
@@ -7512,7 +7577,7 @@ msgid ""
"See the 'Note about fast-forwards' in 'git push --help' for details."
msgstr ""
-#: builtin/push.c:286
+#: builtin/push.c:285
msgid ""
"Updates were rejected because a pushed branch tip is behind its remote\n"
"counterpart. Check out this branch and integrate the remote changes\n"
@@ -7520,7 +7585,7 @@ msgid ""
"See the 'Note about fast-forwards' in 'git push --help' for details."
msgstr ""
-#: builtin/push.c:292
+#: builtin/push.c:291
msgid ""
"Updates were rejected because the remote contains work that you do\n"
"not have locally. This is usually caused by another repository pushing\n"
@@ -7529,33 +7594,33 @@ msgid ""
"See the 'Note about fast-forwards' in 'git push --help' for details."
msgstr ""
-#: builtin/push.c:299
+#: builtin/push.c:298
msgid "Updates were rejected because the tag already exists in the remote."
msgstr ""
-#: builtin/push.c:302
+#: builtin/push.c:301
msgid ""
"You cannot update a remote ref that points at a non-commit object,\n"
"or update a remote ref to make it point at a non-commit object,\n"
"without using the '--force' option.\n"
msgstr ""
-#: builtin/push.c:361
+#: builtin/push.c:360
#, c-format
msgid "Pushing to %s\n"
msgstr ""
-#: builtin/push.c:365
+#: builtin/push.c:364
#, c-format
msgid "failed to push some refs to '%s'"
msgstr ""
-#: builtin/push.c:395
+#: builtin/push.c:394
#, c-format
msgid "bad repository '%s'"
msgstr ""
-#: builtin/push.c:396
+#: builtin/push.c:395
msgid ""
"No configured push destination.\n"
"Either specify the URL from the command-line or configure a remote "
@@ -7568,95 +7633,95 @@ msgid ""
" git push <name>\n"
msgstr ""
-#: builtin/push.c:411
+#: builtin/push.c:410
msgid "--all and --tags are incompatible"
msgstr ""
-#: builtin/push.c:412
+#: builtin/push.c:411
msgid "--all can't be combined with refspecs"
msgstr ""
-#: builtin/push.c:417
+#: builtin/push.c:416
msgid "--mirror and --tags are incompatible"
msgstr ""
-#: builtin/push.c:418
+#: builtin/push.c:417
msgid "--mirror can't be combined with refspecs"
msgstr ""
-#: builtin/push.c:423
+#: builtin/push.c:422
msgid "--all and --mirror are incompatible"
msgstr ""
-#: builtin/push.c:483
+#: builtin/push.c:482
msgid "repository"
msgstr ""
-#: builtin/push.c:484
+#: builtin/push.c:483
msgid "push all refs"
msgstr ""
-#: builtin/push.c:485
+#: builtin/push.c:484
msgid "mirror all refs"
msgstr ""
-#: builtin/push.c:487
+#: builtin/push.c:486
msgid "delete refs"
msgstr ""
-#: builtin/push.c:488
+#: builtin/push.c:487
msgid "push tags (can't be used with --all or --mirror)"
msgstr ""
-#: builtin/push.c:491
+#: builtin/push.c:490
msgid "force updates"
msgstr ""
-#: builtin/push.c:493
+#: builtin/push.c:492
msgid "refname>:<expect"
msgstr ""
-#: builtin/push.c:494
+#: builtin/push.c:493
msgid "require old value of ref to be at this value"
msgstr ""
-#: builtin/push.c:496
+#: builtin/push.c:495
msgid "check"
msgstr ""
-#: builtin/push.c:497
+#: builtin/push.c:496
msgid "control recursive pushing of submodules"
msgstr ""
-#: builtin/push.c:499
+#: builtin/push.c:498
msgid "use thin pack"
msgstr ""
-#: builtin/push.c:500 builtin/push.c:501
+#: builtin/push.c:499 builtin/push.c:500
msgid "receive pack program"
msgstr ""
-#: builtin/push.c:502
+#: builtin/push.c:501
msgid "set upstream for git pull/status"
msgstr ""
-#: builtin/push.c:505
+#: builtin/push.c:504
msgid "prune locally removed refs"
msgstr ""
-#: builtin/push.c:507
+#: builtin/push.c:506
msgid "bypass pre-push hook"
msgstr ""
-#: builtin/push.c:508
+#: builtin/push.c:507
msgid "push missing but relevant tags"
msgstr ""
-#: builtin/push.c:518
+#: builtin/push.c:517
msgid "--delete is incompatible with --all, --mirror and --tags"
msgstr ""
-#: builtin/push.c:520
+#: builtin/push.c:519
msgid "--delete doesn't make sense without any refs"
msgstr ""
@@ -7872,12 +7937,12 @@ msgstr ""
msgid "specifying branches to track makes sense only with fetch mirrors"
msgstr ""
-#: builtin/remote.c:185 builtin/remote.c:641
+#: builtin/remote.c:185 builtin/remote.c:640
#, c-format
msgid "remote %s already exists."
msgstr ""
-#: builtin/remote.c:189 builtin/remote.c:645
+#: builtin/remote.c:189 builtin/remote.c:644
#, c-format
msgid "'%s' is not a valid remote name"
msgstr ""
@@ -7887,45 +7952,45 @@ msgstr ""
msgid "Could not setup master '%s'"
msgstr ""
-#: builtin/remote.c:289
+#: builtin/remote.c:288
#, c-format
msgid "more than one %s"
msgstr ""
-#: builtin/remote.c:334
+#: builtin/remote.c:333
#, c-format
msgid "Could not get fetch map for refspec %s"
msgstr ""
-#: builtin/remote.c:435 builtin/remote.c:443
+#: builtin/remote.c:434 builtin/remote.c:442
msgid "(matching)"
msgstr ""
-#: builtin/remote.c:447
+#: builtin/remote.c:446
msgid "(delete)"
msgstr ""
-#: builtin/remote.c:590 builtin/remote.c:596 builtin/remote.c:602
+#: builtin/remote.c:589 builtin/remote.c:595 builtin/remote.c:601
#, c-format
msgid "Could not append '%s' to '%s'"
msgstr ""
-#: builtin/remote.c:634 builtin/remote.c:787 builtin/remote.c:885
+#: builtin/remote.c:633 builtin/remote.c:794 builtin/remote.c:894
#, c-format
msgid "No such remote: %s"
msgstr ""
-#: builtin/remote.c:651
+#: builtin/remote.c:650
#, c-format
msgid "Could not rename config section '%s' to '%s'"
msgstr ""
-#: builtin/remote.c:657 builtin/remote.c:794
+#: builtin/remote.c:656 builtin/remote.c:846
#, c-format
msgid "Could not remove config section '%s'"
msgstr ""
-#: builtin/remote.c:672
+#: builtin/remote.c:671
#, c-format
msgid ""
"Not updating non-default fetch refspec\n"
@@ -7933,32 +7998,32 @@ msgid ""
"\tPlease update the configuration manually if necessary."
msgstr ""
-#: builtin/remote.c:678
+#: builtin/remote.c:677
#, c-format
msgid "Could not append '%s'"
msgstr ""
-#: builtin/remote.c:689
+#: builtin/remote.c:688
#, c-format
msgid "Could not set '%s'"
msgstr ""
-#: builtin/remote.c:711
+#: builtin/remote.c:710
#, c-format
msgid "deleting '%s' failed"
msgstr ""
-#: builtin/remote.c:745
+#: builtin/remote.c:744
#, c-format
msgid "creating '%s' failed"
msgstr ""
-#: builtin/remote.c:759
+#: builtin/remote.c:765
#, c-format
msgid "Could not remove branch %s"
msgstr ""
-#: builtin/remote.c:829
+#: builtin/remote.c:832
msgid ""
"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
"to delete it, use:"
@@ -7968,363 +8033,419 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: builtin/remote.c:938
+#: builtin/remote.c:947
#, c-format
msgid " new (next fetch will store in remotes/%s)"
msgstr ""
-#: builtin/remote.c:941
+#: builtin/remote.c:950
msgid " tracked"
msgstr ""
-#: builtin/remote.c:943
+#: builtin/remote.c:952
msgid " stale (use 'git remote prune' to remove)"
msgstr ""
-#: builtin/remote.c:945
+#: builtin/remote.c:954
msgid " ???"
msgstr ""
-#: builtin/remote.c:986
+#: builtin/remote.c:995
#, c-format
msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
msgstr ""
-#: builtin/remote.c:993
+#: builtin/remote.c:1002
#, c-format
msgid "rebases onto remote %s"
msgstr ""
-#: builtin/remote.c:996
+#: builtin/remote.c:1005
#, c-format
msgid " merges with remote %s"
msgstr ""
-#: builtin/remote.c:997
+#: builtin/remote.c:1006
msgid " and with remote"
msgstr ""
-#: builtin/remote.c:999
+#: builtin/remote.c:1008
#, c-format
msgid "merges with remote %s"
msgstr ""
-#: builtin/remote.c:1000
+#: builtin/remote.c:1009
msgid " and with remote"
msgstr ""
-#: builtin/remote.c:1046
+#: builtin/remote.c:1055
msgid "create"
msgstr ""
-#: builtin/remote.c:1049
+#: builtin/remote.c:1058
msgid "delete"
msgstr ""
-#: builtin/remote.c:1053
+#: builtin/remote.c:1062
msgid "up to date"
msgstr ""
-#: builtin/remote.c:1056
+#: builtin/remote.c:1065
msgid "fast-forwardable"
msgstr ""
-#: builtin/remote.c:1059
+#: builtin/remote.c:1068
msgid "local out of date"
msgstr ""
-#: builtin/remote.c:1066
+#: builtin/remote.c:1075
#, c-format
msgid " %-*s forces to %-*s (%s)"
msgstr ""
-#: builtin/remote.c:1069
+#: builtin/remote.c:1078
#, c-format
msgid " %-*s pushes to %-*s (%s)"
msgstr ""
-#: builtin/remote.c:1073
+#: builtin/remote.c:1082
#, c-format
msgid " %-*s forces to %s"
msgstr ""
-#: builtin/remote.c:1076
+#: builtin/remote.c:1085
#, c-format
msgid " %-*s pushes to %s"
msgstr ""
-#: builtin/remote.c:1144
+#: builtin/remote.c:1153
msgid "do not query remotes"
msgstr ""
-#: builtin/remote.c:1171
+#: builtin/remote.c:1180
#, c-format
msgid "* remote %s"
msgstr ""
-#: builtin/remote.c:1172
+#: builtin/remote.c:1181
#, c-format
msgid " Fetch URL: %s"
msgstr ""
-#: builtin/remote.c:1173 builtin/remote.c:1318
+#: builtin/remote.c:1182 builtin/remote.c:1329
msgid "(no URL)"
msgstr ""
-#: builtin/remote.c:1182 builtin/remote.c:1184
+#: builtin/remote.c:1191 builtin/remote.c:1193
#, c-format
msgid " Push URL: %s"
msgstr ""
-#: builtin/remote.c:1186 builtin/remote.c:1188 builtin/remote.c:1190
+#: builtin/remote.c:1195 builtin/remote.c:1197 builtin/remote.c:1199
#, c-format
msgid " HEAD branch: %s"
msgstr ""
-#: builtin/remote.c:1192
+#: builtin/remote.c:1201
#, c-format
msgid ""
" HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
msgstr ""
-#: builtin/remote.c:1204
+#: builtin/remote.c:1213
#, c-format
msgid " Remote branch:%s"
msgid_plural " Remote branches:%s"
msgstr[0] ""
msgstr[1] ""
-#: builtin/remote.c:1207 builtin/remote.c:1234
+#: builtin/remote.c:1216 builtin/remote.c:1243
msgid " (status not queried)"
msgstr ""
-#: builtin/remote.c:1216
+#: builtin/remote.c:1225
msgid " Local branch configured for 'git pull':"
msgid_plural " Local branches configured for 'git pull':"
msgstr[0] ""
msgstr[1] ""
-#: builtin/remote.c:1224
+#: builtin/remote.c:1233
msgid " Local refs will be mirrored by 'git push'"
msgstr ""
-#: builtin/remote.c:1231
+#: builtin/remote.c:1240
#, c-format
msgid " Local ref configured for 'git push'%s:"
msgid_plural " Local refs configured for 'git push'%s:"
msgstr[0] ""
msgstr[1] ""
-#: builtin/remote.c:1252
+#: builtin/remote.c:1261
msgid "set refs/remotes/<name>/HEAD according to remote"
msgstr ""
-#: builtin/remote.c:1254
+#: builtin/remote.c:1263
msgid "delete refs/remotes/<name>/HEAD"
msgstr ""
-#: builtin/remote.c:1269
+#: builtin/remote.c:1278
msgid "Cannot determine remote HEAD"
msgstr ""
-#: builtin/remote.c:1271
+#: builtin/remote.c:1280
msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
msgstr ""
-#: builtin/remote.c:1281
+#: builtin/remote.c:1290
#, c-format
msgid "Could not delete %s"
msgstr ""
-#: builtin/remote.c:1289
+#: builtin/remote.c:1298
#, c-format
msgid "Not a valid ref: %s"
msgstr ""
-#: builtin/remote.c:1291
+#: builtin/remote.c:1300
#, c-format
msgid "Could not setup %s"
msgstr ""
-#: builtin/remote.c:1307
+#: builtin/remote.c:1318
#, c-format
msgid " %s will become dangling!"
msgstr ""
-#: builtin/remote.c:1308
+#: builtin/remote.c:1319
#, c-format
msgid " %s has become dangling!"
msgstr ""
-#: builtin/remote.c:1314
+#: builtin/remote.c:1325
#, c-format
msgid "Pruning %s"
msgstr ""
-#: builtin/remote.c:1315
+#: builtin/remote.c:1326
#, c-format
msgid "URL: %s"
msgstr ""
-#: builtin/remote.c:1328
+#: builtin/remote.c:1349
#, c-format
msgid " * [would prune] %s"
msgstr ""
-#: builtin/remote.c:1331
+#: builtin/remote.c:1352
#, c-format
msgid " * [pruned] %s"
msgstr ""
-#: builtin/remote.c:1374
+#: builtin/remote.c:1397
msgid "prune remotes after fetching"
msgstr ""
-#: builtin/remote.c:1440 builtin/remote.c:1514
+#: builtin/remote.c:1463 builtin/remote.c:1537
#, c-format
msgid "No such remote '%s'"
msgstr ""
-#: builtin/remote.c:1460
+#: builtin/remote.c:1483
msgid "add branch"
msgstr ""
-#: builtin/remote.c:1467
+#: builtin/remote.c:1490
msgid "no remote specified"
msgstr ""
-#: builtin/remote.c:1489
+#: builtin/remote.c:1512
msgid "manipulate push URLs"
msgstr ""
-#: builtin/remote.c:1491
+#: builtin/remote.c:1514
msgid "add URL"
msgstr ""
-#: builtin/remote.c:1493
+#: builtin/remote.c:1516
msgid "delete URLs"
msgstr ""
-#: builtin/remote.c:1500
+#: builtin/remote.c:1523
msgid "--add --delete doesn't make sense"
msgstr ""
-#: builtin/remote.c:1540
+#: builtin/remote.c:1563
#, c-format
msgid "Invalid old URL pattern: %s"
msgstr ""
-#: builtin/remote.c:1548
+#: builtin/remote.c:1571
#, c-format
msgid "No such URL found: %s"
msgstr ""
-#: builtin/remote.c:1550
+#: builtin/remote.c:1573
msgid "Will not delete all non-push URLs"
msgstr ""
-#: builtin/remote.c:1564
+#: builtin/remote.c:1587
msgid "be verbose; must be placed before a subcommand"
msgstr ""
-#: builtin/repack.c:16
+#: builtin/repack.c:17
msgid "git repack [options]"
msgstr ""
-#: builtin/repack.c:156
+#: builtin/repack.c:160
msgid "pack everything in a single pack"
msgstr ""
-#: builtin/repack.c:158
+#: builtin/repack.c:162
msgid "same as -a, and turn unreachable objects loose"
msgstr ""
-#: builtin/repack.c:161
+#: builtin/repack.c:165
msgid "remove redundant packs, and run git-prune-packed"
msgstr ""
-#: builtin/repack.c:163
+#: builtin/repack.c:167
msgid "pass --no-reuse-delta to git-pack-objects"
msgstr ""
-#: builtin/repack.c:165
+#: builtin/repack.c:169
msgid "pass --no-reuse-object to git-pack-objects"
msgstr ""
-#: builtin/repack.c:167
+#: builtin/repack.c:171
msgid "do not run git-update-server-info"
msgstr ""
-#: builtin/repack.c:170
+#: builtin/repack.c:174
msgid "pass --local to git-pack-objects"
msgstr ""
-#: builtin/repack.c:172
+#: builtin/repack.c:176
msgid "write bitmap index"
msgstr ""
-#: builtin/repack.c:173
+#: builtin/repack.c:177
msgid "approxidate"
msgstr ""
-#: builtin/repack.c:174
+#: builtin/repack.c:178
msgid "with -A, do not loosen objects older than this"
msgstr ""
-#: builtin/repack.c:176
+#: builtin/repack.c:180
msgid "size of the window used for delta compression"
msgstr ""
-#: builtin/repack.c:177 builtin/repack.c:181
+#: builtin/repack.c:181 builtin/repack.c:185
msgid "bytes"
msgstr ""
-#: builtin/repack.c:178
+#: builtin/repack.c:182
msgid "same as the above, but limit memory size instead of entries count"
msgstr ""
-#: builtin/repack.c:180
+#: builtin/repack.c:184
msgid "limits the maximum delta depth"
msgstr ""
-#: builtin/repack.c:182
+#: builtin/repack.c:186
msgid "maximum size of each packfile"
msgstr ""
-#: builtin/repack.c:184
+#: builtin/repack.c:188
msgid "repack objects in packs marked with .keep"
msgstr ""
-#: builtin/repack.c:371
+#: builtin/repack.c:374
#, c-format
msgid "removing '%s' failed"
msgstr ""
-#: builtin/replace.c:17
+#: builtin/replace.c:19
msgid "git replace [-f] <object> <replacement>"
msgstr ""
-#: builtin/replace.c:18
+#: builtin/replace.c:20
+msgid "git replace [-f] --edit <object>"
+msgstr ""
+
+#: builtin/replace.c:21
+msgid "git replace [-f] --graft <commit> [<parent>...]"
+msgstr ""
+
+#: builtin/replace.c:22
msgid "git replace -d <object>..."
msgstr ""
-#: builtin/replace.c:19
+#: builtin/replace.c:23
msgid "git replace [--format=<format>] [-l [<pattern>]]"
msgstr ""
-#: builtin/replace.c:174
+#: builtin/replace.c:322 builtin/replace.c:360 builtin/replace.c:388
+#, c-format
+msgid "Not a valid object name: '%s'"
+msgstr ""
+
+#: builtin/replace.c:352
+#, c-format
+msgid "bad mergetag in commit '%s'"
+msgstr ""
+
+#: builtin/replace.c:354
+#, c-format
+msgid "malformed mergetag in commit '%s'"
+msgstr ""
+
+#: builtin/replace.c:365
+#, c-format
+msgid ""
+"original commit '%s' contains mergetag '%s' that is discarded; use --edit "
+"instead of --graft"
+msgstr ""
+
+#: builtin/replace.c:398
+#, c-format
+msgid "the original commit '%s' has a gpg signature."
+msgstr ""
+
+#: builtin/replace.c:399
+msgid "the signature will be removed in the replacement commit!"
+msgstr ""
+
+#: builtin/replace.c:405
+#, c-format
+msgid "could not write replacement commit for: '%s'"
+msgstr ""
+
+#: builtin/replace.c:429
msgid "list replace refs"
msgstr ""
-#: builtin/replace.c:175
+#: builtin/replace.c:430
msgid "delete replace refs"
msgstr ""
-#: builtin/replace.c:176
+#: builtin/replace.c:431
+msgid "edit existing object"
+msgstr ""
+
+#: builtin/replace.c:432
+msgid "change a commit's parents"
+msgstr ""
+
+#: builtin/replace.c:433
msgid "replace the ref if it exists"
msgstr ""
-#: builtin/replace.c:177
+#: builtin/replace.c:434
+msgid "do not pretty-print contents for --edit"
+msgstr ""
+
+#: builtin/replace.c:435
msgid "use this format"
msgstr ""
@@ -8392,97 +8513,97 @@ msgstr ""
msgid "Cannot do a %s reset in the middle of a merge."
msgstr ""
-#: builtin/reset.c:273
+#: builtin/reset.c:275
msgid "be quiet, only report errors"
msgstr ""
-#: builtin/reset.c:275
+#: builtin/reset.c:277
msgid "reset HEAD and index"
msgstr ""
-#: builtin/reset.c:276
+#: builtin/reset.c:278
msgid "reset only HEAD"
msgstr ""
-#: builtin/reset.c:278 builtin/reset.c:280
+#: builtin/reset.c:280 builtin/reset.c:282
msgid "reset HEAD, index and working tree"
msgstr ""
-#: builtin/reset.c:282
+#: builtin/reset.c:284
msgid "reset HEAD but keep local changes"
msgstr ""
-#: builtin/reset.c:285
+#: builtin/reset.c:287
msgid "record only the fact that removed paths will be added later"
msgstr ""
-#: builtin/reset.c:302
+#: builtin/reset.c:304
#, c-format
msgid "Failed to resolve '%s' as a valid revision."
msgstr ""
-#: builtin/reset.c:305 builtin/reset.c:313
+#: builtin/reset.c:307 builtin/reset.c:315
#, c-format
msgid "Could not parse object '%s'."
msgstr ""
-#: builtin/reset.c:310
+#: builtin/reset.c:312
#, c-format
msgid "Failed to resolve '%s' as a valid tree."
msgstr ""
-#: builtin/reset.c:319
+#: builtin/reset.c:321
msgid "--patch is incompatible with --{hard,mixed,soft}"
msgstr ""
-#: builtin/reset.c:328
+#: builtin/reset.c:330
msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
msgstr ""
-#: builtin/reset.c:330
+#: builtin/reset.c:332
#, c-format
msgid "Cannot do %s reset with paths."
msgstr ""
-#: builtin/reset.c:340
+#: builtin/reset.c:342
#, c-format
msgid "%s reset is not allowed in a bare repository"
msgstr ""
-#: builtin/reset.c:344
+#: builtin/reset.c:346
msgid "-N can only be used with --mixed"
msgstr ""
-#: builtin/reset.c:361
+#: builtin/reset.c:363
msgid "Unstaged changes after reset:"
msgstr ""
-#: builtin/reset.c:367
+#: builtin/reset.c:369
#, c-format
msgid "Could not reset index file to revision '%s'."
msgstr ""
-#: builtin/reset.c:372
+#: builtin/reset.c:373
msgid "Could not write new index file."
msgstr ""
-#: builtin/rev-parse.c:359
+#: builtin/rev-parse.c:360
msgid "git rev-parse --parseopt [options] -- [<args>...]"
msgstr ""
-#: builtin/rev-parse.c:364
+#: builtin/rev-parse.c:365
msgid "keep the `--` passed as an arg"
msgstr ""
-#: builtin/rev-parse.c:366
+#: builtin/rev-parse.c:367
msgid "stop parsing after the first non-option argument"
msgstr ""
-#: builtin/rev-parse.c:369
+#: builtin/rev-parse.c:370
msgid "output in stuck long form"
msgstr ""
-#: builtin/rev-parse.c:497
+#: builtin/rev-parse.c:498
msgid ""
"git rev-parse --parseopt [options] -- [<args>...]\n"
" or: git rev-parse --sq-quote [<arg>...]\n"
@@ -8831,11 +8952,11 @@ msgstr ""
msgid "shorten ref output"
msgstr ""
-#: builtin/symbolic-ref.c:43 builtin/update-ref.c:255
+#: builtin/symbolic-ref.c:43 builtin/update-ref.c:349
msgid "reason"
msgstr ""
-#: builtin/symbolic-ref.c:43 builtin/update-ref.c:255
+#: builtin/symbolic-ref.c:43 builtin/update-ref.c:349
msgid "reason of the update"
msgstr ""
@@ -8858,196 +8979,203 @@ msgstr ""
msgid "git tag -v <tagname>..."
msgstr ""
-#: builtin/tag.c:67
+#: builtin/tag.c:69
#, c-format
msgid "malformed object at '%s'"
msgstr ""
-#: builtin/tag.c:239
+#: builtin/tag.c:301
#, c-format
msgid "tag name too long: %.*s..."
msgstr ""
-#: builtin/tag.c:244
+#: builtin/tag.c:306
#, c-format
msgid "tag '%s' not found."
msgstr ""
-#: builtin/tag.c:259
+#: builtin/tag.c:321
#, c-format
msgid "Deleted tag '%s' (was %s)\n"
msgstr ""
-#: builtin/tag.c:271
+#: builtin/tag.c:333
#, c-format
msgid "could not verify the tag '%s'"
msgstr ""
-#: builtin/tag.c:281
+#: builtin/tag.c:343
#, c-format
msgid ""
"\n"
-"Write a tag message\n"
+"Write a message for tag:\n"
+" %s\n"
"Lines starting with '%c' will be ignored.\n"
msgstr ""
-#: builtin/tag.c:285
+#: builtin/tag.c:347
#, c-format
msgid ""
"\n"
-"Write a tag message\n"
+"Write a message for tag:\n"
+" %s\n"
"Lines starting with '%c' will be kept; you may remove them yourself if you "
"want to.\n"
msgstr ""
-#: builtin/tag.c:324
+#: builtin/tag.c:371
+#, c-format
+msgid "unsupported sort specification '%s'"
+msgstr ""
+
+#: builtin/tag.c:373
+#, c-format
+msgid "unsupported sort specification '%s' in variable '%s'"
+msgstr ""
+
+#: builtin/tag.c:428
msgid "unable to sign the tag"
msgstr ""
-#: builtin/tag.c:326
+#: builtin/tag.c:430
msgid "unable to write tag file"
msgstr ""
-#: builtin/tag.c:351
+#: builtin/tag.c:455
msgid "bad object type."
msgstr ""
-#: builtin/tag.c:364
+#: builtin/tag.c:468
msgid "tag header too big."
msgstr ""
-#: builtin/tag.c:400
+#: builtin/tag.c:504
msgid "no tag message?"
msgstr ""
-#: builtin/tag.c:406
+#: builtin/tag.c:510
#, c-format
msgid "The tag message has been left in %s\n"
msgstr ""
-#: builtin/tag.c:455
+#: builtin/tag.c:559
msgid "switch 'points-at' requires an object"
msgstr ""
-#: builtin/tag.c:457
+#: builtin/tag.c:561
#, c-format
msgid "malformed object name '%s'"
msgstr ""
-#: builtin/tag.c:480
-#, c-format
-msgid "unsupported sort specification %s"
-msgstr ""
-
-#: builtin/tag.c:500
+#: builtin/tag.c:588
msgid "list tag names"
msgstr ""
-#: builtin/tag.c:502
+#: builtin/tag.c:590
msgid "print <n> lines of each tag message"
msgstr ""
-#: builtin/tag.c:504
+#: builtin/tag.c:592
msgid "delete tags"
msgstr ""
-#: builtin/tag.c:505
+#: builtin/tag.c:593
msgid "verify tags"
msgstr ""
-#: builtin/tag.c:507
+#: builtin/tag.c:595
msgid "Tag creation options"
msgstr ""
-#: builtin/tag.c:509
+#: builtin/tag.c:597
msgid "annotated tag, needs a message"
msgstr ""
-#: builtin/tag.c:511
+#: builtin/tag.c:599
msgid "tag message"
msgstr ""
-#: builtin/tag.c:513
+#: builtin/tag.c:601
msgid "annotated and GPG-signed tag"
msgstr ""
-#: builtin/tag.c:517
+#: builtin/tag.c:605
msgid "use another key to sign the tag"
msgstr ""
-#: builtin/tag.c:518
+#: builtin/tag.c:606
msgid "replace the tag if exists"
msgstr ""
-#: builtin/tag.c:519
+#: builtin/tag.c:607
msgid "show tag list in columns"
msgstr ""
-#: builtin/tag.c:521
+#: builtin/tag.c:609
msgid "sort tags"
msgstr ""
-#: builtin/tag.c:525
+#: builtin/tag.c:613
msgid "Tag listing options"
msgstr ""
-#: builtin/tag.c:528 builtin/tag.c:534
+#: builtin/tag.c:616 builtin/tag.c:622
msgid "print only tags that contain the commit"
msgstr ""
-#: builtin/tag.c:540
+#: builtin/tag.c:628
msgid "print only tags of the object"
msgstr ""
-#: builtin/tag.c:566
+#: builtin/tag.c:654
msgid "--column and -n are incompatible"
msgstr ""
-#: builtin/tag.c:578
+#: builtin/tag.c:666
msgid "--sort and -n are incompatible"
msgstr ""
-#: builtin/tag.c:585
+#: builtin/tag.c:673
msgid "-n option is only allowed with -l."
msgstr ""
-#: builtin/tag.c:587
+#: builtin/tag.c:675
msgid "--contains option is only allowed with -l."
msgstr ""
-#: builtin/tag.c:589
+#: builtin/tag.c:677
msgid "--points-at option is only allowed with -l."
msgstr ""
-#: builtin/tag.c:597
+#: builtin/tag.c:685
msgid "only one -F or -m option is allowed."
msgstr ""
-#: builtin/tag.c:617
+#: builtin/tag.c:705
msgid "too many params"
msgstr ""
-#: builtin/tag.c:623
+#: builtin/tag.c:711
#, c-format
msgid "'%s' is not a valid tag name."
msgstr ""
-#: builtin/tag.c:628
+#: builtin/tag.c:716
#, c-format
msgid "tag '%s' already exists"
msgstr ""
-#: builtin/tag.c:646
+#: builtin/tag.c:734
#, c-format
msgid "%s: cannot lock the ref"
msgstr ""
-#: builtin/tag.c:648
+#: builtin/tag.c:736
#, c-format
msgid "%s: cannot update the ref"
msgstr ""
-#: builtin/tag.c:650
+#: builtin/tag.c:738
#, c-format
msgid "Updated tag '%s' (was %s)\n"
msgstr ""
@@ -9056,118 +9184,122 @@ msgstr ""
msgid "Unpacking objects"
msgstr ""
-#: builtin/update-index.c:399
+#: builtin/update-index.c:402
msgid "git update-index [options] [--] [<file>...]"
msgstr ""
-#: builtin/update-index.c:748
+#: builtin/update-index.c:755
msgid "continue refresh even when index needs update"
msgstr ""
-#: builtin/update-index.c:751
+#: builtin/update-index.c:758
msgid "refresh: ignore submodules"
msgstr ""
-#: builtin/update-index.c:754
+#: builtin/update-index.c:761
msgid "do not ignore new files"
msgstr ""
-#: builtin/update-index.c:756
+#: builtin/update-index.c:763
msgid "let files replace directories and vice-versa"
msgstr ""
-#: builtin/update-index.c:758
+#: builtin/update-index.c:765
msgid "notice files missing from worktree"
msgstr ""
-#: builtin/update-index.c:760
+#: builtin/update-index.c:767
msgid "refresh even if index contains unmerged entries"
msgstr ""
-#: builtin/update-index.c:763
+#: builtin/update-index.c:770
msgid "refresh stat information"
msgstr ""
-#: builtin/update-index.c:767
+#: builtin/update-index.c:774
msgid "like --refresh, but ignore assume-unchanged setting"
msgstr ""
-#: builtin/update-index.c:771
+#: builtin/update-index.c:778
msgid "<mode>,<object>,<path>"
msgstr ""
-#: builtin/update-index.c:772
+#: builtin/update-index.c:779
msgid "add the specified entry to the index"
msgstr ""
-#: builtin/update-index.c:776
+#: builtin/update-index.c:783
msgid "(+/-)x"
msgstr ""
-#: builtin/update-index.c:777
+#: builtin/update-index.c:784
msgid "override the executable bit of the listed files"
msgstr ""
-#: builtin/update-index.c:781
+#: builtin/update-index.c:788
msgid "mark files as \"not changing\""
msgstr ""
-#: builtin/update-index.c:784
+#: builtin/update-index.c:791
msgid "clear assumed-unchanged bit"
msgstr ""
-#: builtin/update-index.c:787
+#: builtin/update-index.c:794
msgid "mark files as \"index-only\""
msgstr ""
-#: builtin/update-index.c:790
+#: builtin/update-index.c:797
msgid "clear skip-worktree bit"
msgstr ""
-#: builtin/update-index.c:793
+#: builtin/update-index.c:800
msgid "add to index only; do not add content to object database"
msgstr ""
-#: builtin/update-index.c:795
+#: builtin/update-index.c:802
msgid "remove named paths even if present in worktree"
msgstr ""
-#: builtin/update-index.c:797
+#: builtin/update-index.c:804
msgid "with --stdin: input lines are terminated by null bytes"
msgstr ""
-#: builtin/update-index.c:799
+#: builtin/update-index.c:806
msgid "read list of paths to be updated from standard input"
msgstr ""
-#: builtin/update-index.c:803
+#: builtin/update-index.c:810
msgid "add entries from standard input to the index"
msgstr ""
-#: builtin/update-index.c:807
+#: builtin/update-index.c:814
msgid "repopulate stages #2 and #3 for the listed paths"
msgstr ""
-#: builtin/update-index.c:811
+#: builtin/update-index.c:818
msgid "only update entries that differ from HEAD"
msgstr ""
-#: builtin/update-index.c:815
+#: builtin/update-index.c:822
msgid "ignore files missing from worktree"
msgstr ""
-#: builtin/update-index.c:818
+#: builtin/update-index.c:825
msgid "report actions to standard output"
msgstr ""
-#: builtin/update-index.c:820
+#: builtin/update-index.c:827
msgid "(for porcelains) forget saved unresolved conflicts"
msgstr ""
-#: builtin/update-index.c:824
+#: builtin/update-index.c:831
msgid "write index in this format"
msgstr ""
+#: builtin/update-index.c:833
+msgid "enable or disable split index"
+msgstr ""
+
#: builtin/update-ref.c:9
msgid "git update-ref [options] -d <refname> [<oldval>]"
msgstr ""
@@ -9180,19 +9312,19 @@ msgstr ""
msgid "git update-ref [options] --stdin [-z]"
msgstr ""
-#: builtin/update-ref.c:256
+#: builtin/update-ref.c:350
msgid "delete the reference"
msgstr ""
-#: builtin/update-ref.c:258
+#: builtin/update-ref.c:352
msgid "update <refname> not the one it points to"
msgstr ""
-#: builtin/update-ref.c:259
+#: builtin/update-ref.c:353
msgid "stdin has NUL-terminated arguments"
msgstr ""
-#: builtin/update-ref.c:260
+#: builtin/update-ref.c:354
msgid "read updates from stdin"
msgstr ""
@@ -9204,15 +9336,23 @@ msgstr ""
msgid "update the info files from scratch"
msgstr ""
-#: builtin/verify-pack.c:56
+#: builtin/verify-commit.c:17
+msgid "git verify-commit [-v|--verbose] <commit>..."
+msgstr ""
+
+#: builtin/verify-commit.c:75
+msgid "print commit contents"
+msgstr ""
+
+#: builtin/verify-pack.c:55
msgid "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>..."
msgstr ""
-#: builtin/verify-pack.c:66
+#: builtin/verify-pack.c:65
msgid "verbose"
msgstr ""
-#: builtin/verify-pack.c:68
+#: builtin/verify-pack.c:67
msgid "show statistics only"
msgstr ""
@@ -9657,11 +9797,11 @@ msgstr ""
msgid "Pull is not possible because you have unmerged files."
msgstr ""
-#: git-pull.sh:247
+#: git-pull.sh:245
msgid "updating an unborn branch with changes added to the index"
msgstr ""
-#: git-pull.sh:271
+#: git-pull.sh:269
#, sh-format
msgid ""
"Warning: fetch updated the current branch head.\n"
@@ -9669,11 +9809,11 @@ msgid ""
"Warning: commit $orig_head."
msgstr ""
-#: git-pull.sh:296
+#: git-pull.sh:294
msgid "Cannot merge multiple branches into empty head"
msgstr ""
-#: git-pull.sh:300
+#: git-pull.sh:298
msgid "Cannot rebase onto multiple branches"
msgstr ""
@@ -9685,58 +9825,58 @@ msgid ""
"\"."
msgstr ""
-#: git-rebase.sh:164
+#: git-rebase.sh:165
msgid "Applied autostash."
msgstr ""
-#: git-rebase.sh:167
+#: git-rebase.sh:168
#, sh-format
msgid "Cannot store $stash_sha1"
msgstr ""
-#: git-rebase.sh:168
+#: git-rebase.sh:169
msgid ""
"Applying autostash resulted in conflicts.\n"
"Your changes are safe in the stash.\n"
"You can run \"git stash pop\" or \"git stash drop\" at any time.\n"
msgstr ""
-#: git-rebase.sh:207
+#: git-rebase.sh:208
msgid "The pre-rebase hook refused to rebase."
msgstr ""
-#: git-rebase.sh:212
+#: git-rebase.sh:213
msgid "It looks like git-am is in progress. Cannot rebase."
msgstr ""
-#: git-rebase.sh:350
+#: git-rebase.sh:351
msgid "The --exec option must be used with the --interactive option"
msgstr ""
-#: git-rebase.sh:355
+#: git-rebase.sh:356
msgid "No rebase in progress?"
msgstr ""
-#: git-rebase.sh:366
+#: git-rebase.sh:367
msgid "The --edit-todo action can only be used during interactive rebase."
msgstr ""
-#: git-rebase.sh:373
+#: git-rebase.sh:374
msgid "Cannot read HEAD"
msgstr ""
-#: git-rebase.sh:376
+#: git-rebase.sh:377
msgid ""
"You must edit all merge conflicts and then\n"
"mark them as resolved using git add"
msgstr ""
-#: git-rebase.sh:394
+#: git-rebase.sh:395
#, sh-format
msgid "Could not move back to $head_name"
msgstr ""
-#: git-rebase.sh:413
+#: git-rebase.sh:414
#, sh-format
msgid ""
"It seems that there is already a $state_dir_base directory, and\n"
@@ -9749,64 +9889,64 @@ msgid ""
"valuable there."
msgstr ""
-#: git-rebase.sh:464
+#: git-rebase.sh:465
#, sh-format
msgid "invalid upstream $upstream_name"
msgstr ""
-#: git-rebase.sh:488
+#: git-rebase.sh:489
#, sh-format
msgid "$onto_name: there are more than one merge bases"
msgstr ""
-#: git-rebase.sh:491 git-rebase.sh:495
+#: git-rebase.sh:492 git-rebase.sh:496
#, sh-format
msgid "$onto_name: there is no merge base"
msgstr ""
-#: git-rebase.sh:500
+#: git-rebase.sh:501
#, sh-format
msgid "Does not point to a valid commit: $onto_name"
msgstr ""
-#: git-rebase.sh:523
+#: git-rebase.sh:524
#, sh-format
msgid "fatal: no such branch: $branch_name"
msgstr ""
-#: git-rebase.sh:556
+#: git-rebase.sh:557
msgid "Cannot autostash"
msgstr ""
-#: git-rebase.sh:561
+#: git-rebase.sh:562
#, sh-format
msgid "Created autostash: $stash_abbrev"
msgstr ""
-#: git-rebase.sh:565
+#: git-rebase.sh:566
msgid "Please commit or stash them."
msgstr ""
-#: git-rebase.sh:585
+#: git-rebase.sh:586
#, sh-format
msgid "Current branch $branch_name is up to date."
msgstr ""
-#: git-rebase.sh:589
+#: git-rebase.sh:590
#, sh-format
msgid "Current branch $branch_name is up to date, rebase forced."
msgstr ""
-#: git-rebase.sh:600
+#: git-rebase.sh:601
#, sh-format
msgid "Changes from $mb to $onto:"
msgstr ""
-#: git-rebase.sh:609
+#: git-rebase.sh:610
msgid "First, rewinding head to replay your work on top of it..."
msgstr ""
-#: git-rebase.sh:619
+#: git-rebase.sh:620
#, sh-format
msgid "Fast-forwarded $branch_name to $onto_name."
msgstr ""
@@ -9823,23 +9963,23 @@ msgstr ""
msgid "Cannot save the current index state"
msgstr ""
-#: git-stash.sh:123 git-stash.sh:136
+#: git-stash.sh:124 git-stash.sh:137
msgid "Cannot save the current worktree state"
msgstr ""
-#: git-stash.sh:140
+#: git-stash.sh:141
msgid "No changes selected"
msgstr ""
-#: git-stash.sh:143
+#: git-stash.sh:144
msgid "Cannot remove temporary index (can't happen)"
msgstr ""
-#: git-stash.sh:156
+#: git-stash.sh:157
msgid "Cannot record working tree state"
msgstr ""
-#: git-stash.sh:190
+#: git-stash.sh:191
#, sh-format
msgid "Cannot update $ref_stash with $w_commit"
msgstr ""
@@ -9854,92 +9994,92 @@ msgstr ""
#. $ git stash save --blah-blah 2>&1 | head -n 2
#. error: unknown option for 'stash save': --blah-blah
#. To provide a message, use git stash save -- '--blah-blah'
-#: git-stash.sh:237
+#: git-stash.sh:238
#, sh-format
msgid ""
"error: unknown option for 'stash save': $option\n"
" To provide a message, use git stash save -- '$option'"
msgstr ""
-#: git-stash.sh:258
+#: git-stash.sh:259
msgid "No local changes to save"
msgstr ""
-#: git-stash.sh:262
+#: git-stash.sh:263
msgid "Cannot initialize stash"
msgstr ""
-#: git-stash.sh:266
+#: git-stash.sh:267
msgid "Cannot save the current status"
msgstr ""
-#: git-stash.sh:284
+#: git-stash.sh:285
msgid "Cannot remove worktree changes"
msgstr ""
-#: git-stash.sh:383
+#: git-stash.sh:384
msgid "No stash found."
msgstr ""
-#: git-stash.sh:390
+#: git-stash.sh:391
#, sh-format
msgid "Too many revisions specified: $REV"
msgstr ""
-#: git-stash.sh:396
+#: git-stash.sh:397
#, sh-format
msgid "$reference is not valid reference"
msgstr ""
-#: git-stash.sh:424
+#: git-stash.sh:425
#, sh-format
msgid "'$args' is not a stash-like commit"
msgstr ""
-#: git-stash.sh:435
+#: git-stash.sh:436
#, sh-format
msgid "'$args' is not a stash reference"
msgstr ""
-#: git-stash.sh:443
+#: git-stash.sh:444
msgid "unable to refresh index"
msgstr ""
-#: git-stash.sh:447
+#: git-stash.sh:448
msgid "Cannot apply a stash in the middle of a merge"
msgstr ""
-#: git-stash.sh:455
+#: git-stash.sh:456
msgid "Conflicts in index. Try without --index."
msgstr ""
-#: git-stash.sh:457
+#: git-stash.sh:458
msgid "Could not save index tree"
msgstr ""
-#: git-stash.sh:491
+#: git-stash.sh:492
msgid "Cannot unstage modified files"
msgstr ""
-#: git-stash.sh:506
+#: git-stash.sh:507
msgid "Index was not unstashed."
msgstr ""
-#: git-stash.sh:529
+#: git-stash.sh:530
#, sh-format
msgid "Dropped ${REV} ($s)"
msgstr ""
-#: git-stash.sh:530
+#: git-stash.sh:531
#, sh-format
msgid "${REV}: Could not drop stash entry"
msgstr ""
-#: git-stash.sh:537
+#: git-stash.sh:538
msgid "No branch name specified"
msgstr ""
-#: git-stash.sh:609
+#: git-stash.sh:610
msgid "(To restore them type \"git stash apply\")"
msgstr ""
@@ -9958,26 +10098,26 @@ msgstr ""
msgid "Clone of '$url' into submodule path '$sm_path' failed"
msgstr ""
-#: git-submodule.sh:299
+#: git-submodule.sh:296
#, sh-format
msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
msgstr ""
-#: git-submodule.sh:409
+#: git-submodule.sh:406
msgid "Relative path can only be used from the toplevel of the working tree"
msgstr ""
-#: git-submodule.sh:419
+#: git-submodule.sh:416
#, sh-format
msgid "repo URL: '$repo' must be absolute or begin with ./|../"
msgstr ""
-#: git-submodule.sh:436
+#: git-submodule.sh:433
#, sh-format
msgid "'$sm_path' already exists in the index"
msgstr ""
-#: git-submodule.sh:440
+#: git-submodule.sh:437
#, sh-format
msgid ""
"The following path is ignored by one of your .gitignore files:\n"
@@ -9985,235 +10125,235 @@ msgid ""
"Use -f if you really want to add it."
msgstr ""
-#: git-submodule.sh:458
+#: git-submodule.sh:455
#, sh-format
msgid "Adding existing repo at '$sm_path' to the index"
msgstr ""
-#: git-submodule.sh:460
+#: git-submodule.sh:457
#, sh-format
msgid "'$sm_path' already exists and is not a valid git repo"
msgstr ""
-#: git-submodule.sh:468
+#: git-submodule.sh:465
#, sh-format
msgid "A git directory for '$sm_name' is found locally with remote(s):"
msgstr ""
-#: git-submodule.sh:470
+#: git-submodule.sh:467
#, sh-format
msgid ""
"If you want to reuse this local git directory instead of cloning again from"
msgstr ""
-#: git-submodule.sh:472
+#: git-submodule.sh:469
#, sh-format
msgid ""
"use the '--force' option. If the local git directory is not the correct repo"
msgstr ""
-#: git-submodule.sh:473
+#: git-submodule.sh:470
#, sh-format
msgid ""
"or you are unsure what this means choose another name with the '--name' "
"option."
msgstr ""
-#: git-submodule.sh:475
+#: git-submodule.sh:472
#, sh-format
msgid "Reactivating local git directory for submodule '$sm_name'."
msgstr ""
-#: git-submodule.sh:487
+#: git-submodule.sh:484
#, sh-format
msgid "Unable to checkout submodule '$sm_path'"
msgstr ""
-#: git-submodule.sh:492
+#: git-submodule.sh:489
#, sh-format
msgid "Failed to add submodule '$sm_path'"
msgstr ""
-#: git-submodule.sh:501
+#: git-submodule.sh:498
#, sh-format
msgid "Failed to register submodule '$sm_path'"
msgstr ""
-#: git-submodule.sh:545
+#: git-submodule.sh:542
#, sh-format
msgid "Entering '$prefix$displaypath'"
msgstr ""
-#: git-submodule.sh:565
+#: git-submodule.sh:562
#, sh-format
msgid "Stopping at '$prefix$displaypath'; script returned non-zero status."
msgstr ""
-#: git-submodule.sh:611
+#: git-submodule.sh:608
#, sh-format
msgid "No url found for submodule path '$displaypath' in .gitmodules"
msgstr ""
-#: git-submodule.sh:620
+#: git-submodule.sh:617
#, sh-format
msgid "Failed to register url for submodule path '$displaypath'"
msgstr ""
-#: git-submodule.sh:622
+#: git-submodule.sh:619
#, sh-format
msgid "Submodule '$name' ($url) registered for path '$displaypath'"
msgstr ""
-#: git-submodule.sh:639
+#: git-submodule.sh:636
#, sh-format
msgid "Failed to register update mode for submodule path '$displaypath'"
msgstr ""
-#: git-submodule.sh:677
+#: git-submodule.sh:674
#, sh-format
msgid "Use '.' if you really want to deinitialize all submodules"
msgstr ""
-#: git-submodule.sh:694
+#: git-submodule.sh:691
#, sh-format
msgid "Submodule work tree '$displaypath' contains a .git directory"
msgstr ""
-#: git-submodule.sh:695
+#: git-submodule.sh:692
#, sh-format
msgid ""
"(use 'rm -rf' if you really want to remove it including all of its history)"
msgstr ""
-#: git-submodule.sh:701
+#: git-submodule.sh:698
#, sh-format
msgid ""
"Submodule work tree '$displaypath' contains local modifications; use '-f' to "
"discard them"
msgstr ""
-#: git-submodule.sh:704
+#: git-submodule.sh:701
#, sh-format
msgid "Cleared directory '$displaypath'"
msgstr ""
-#: git-submodule.sh:705
+#: git-submodule.sh:702
#, sh-format
msgid "Could not remove submodule work tree '$displaypath'"
msgstr ""
-#: git-submodule.sh:708
+#: git-submodule.sh:705
#, sh-format
msgid "Could not create empty submodule directory '$displaypath'"
msgstr ""
-#: git-submodule.sh:717
+#: git-submodule.sh:714
#, sh-format
msgid "Submodule '$name' ($url) unregistered for path '$displaypath'"
msgstr ""
-#: git-submodule.sh:833
+#: git-submodule.sh:830
#, sh-format
msgid ""
"Submodule path '$displaypath' not initialized\n"
"Maybe you want to use 'update --init'?"
msgstr ""
-#: git-submodule.sh:846
+#: git-submodule.sh:843
#, sh-format
msgid "Unable to find current revision in submodule path '$displaypath'"
msgstr ""
-#: git-submodule.sh:855
+#: git-submodule.sh:852
#, sh-format
msgid "Unable to fetch in submodule path '$sm_path'"
msgstr ""
-#: git-submodule.sh:879
+#: git-submodule.sh:876
#, sh-format
msgid "Unable to fetch in submodule path '$displaypath'"
msgstr ""
-#: git-submodule.sh:893
+#: git-submodule.sh:890
#, sh-format
msgid "Unable to checkout '$sha1' in submodule path '$displaypath'"
msgstr ""
-#: git-submodule.sh:894
+#: git-submodule.sh:891
#, sh-format
msgid "Submodule path '$displaypath': checked out '$sha1'"
msgstr ""
-#: git-submodule.sh:898
+#: git-submodule.sh:895
#, sh-format
msgid "Unable to rebase '$sha1' in submodule path '$displaypath'"
msgstr ""
-#: git-submodule.sh:899
+#: git-submodule.sh:896
#, sh-format
msgid "Submodule path '$displaypath': rebased into '$sha1'"
msgstr ""
-#: git-submodule.sh:904
+#: git-submodule.sh:901
#, sh-format
msgid "Unable to merge '$sha1' in submodule path '$displaypath'"
msgstr ""
-#: git-submodule.sh:905
+#: git-submodule.sh:902
#, sh-format
msgid "Submodule path '$displaypath': merged in '$sha1'"
msgstr ""
-#: git-submodule.sh:910
+#: git-submodule.sh:907
#, sh-format
msgid ""
"Execution of '$command $sha1' failed in submodule path '$prefix$sm_path'"
msgstr ""
-#: git-submodule.sh:911
+#: git-submodule.sh:908
#, sh-format
msgid "Submodule path '$prefix$sm_path': '$command $sha1'"
msgstr ""
-#: git-submodule.sh:941
+#: git-submodule.sh:938
#, sh-format
msgid "Failed to recurse into submodule path '$displaypath'"
msgstr ""
-#: git-submodule.sh:1049
+#: git-submodule.sh:1046
msgid "The --cached option cannot be used with the --files option"
msgstr ""
-#: git-submodule.sh:1097
+#: git-submodule.sh:1098
#, sh-format
msgid "unexpected mode $mod_dst"
msgstr ""
-#: git-submodule.sh:1117
+#: git-submodule.sh:1118
#, sh-format
msgid " Warn: $display_name doesn't contain commit $sha1_src"
msgstr ""
-#: git-submodule.sh:1120
+#: git-submodule.sh:1121
#, sh-format
msgid " Warn: $display_name doesn't contain commit $sha1_dst"
msgstr ""
-#: git-submodule.sh:1123
+#: git-submodule.sh:1124
#, sh-format
msgid " Warn: $display_name doesn't contain commits $sha1_src and $sha1_dst"
msgstr ""
-#: git-submodule.sh:1148
+#: git-submodule.sh:1149
msgid "blob"
msgstr ""
-#: git-submodule.sh:1262
+#: git-submodule.sh:1267
#, sh-format
msgid "Failed to recurse into submodule path '$sm_path'"
msgstr ""
-#: git-submodule.sh:1326
+#: git-submodule.sh:1331
#, sh-format
msgid "Synchronizing submodule url for '$displaypath'"
msgstr ""
diff --git a/preload-index.c b/preload-index.c
index 968ee25eae..c1fe3a3ef9 100644
--- a/preload-index.c
+++ b/preload-index.c
@@ -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;
}
diff --git a/pretty.c b/pretty.c
index 4f512876ce..3a1da6fd32 100644
--- a/pretty.c
+++ b/pretty.c
@@ -40,10 +40,9 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
const char *fmt;
int i;
- if (!starts_with(var, "pretty."))
+ if (!skip_prefix(var, "pretty.", &name))
return 0;
- name = var + strlen("pretty.");
for (i = 0; i < builtin_formats_len; i++) {
if (!strcmp(commit_formats[i].name, name))
return 0;
@@ -274,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)
@@ -606,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 =
@@ -652,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 {
/*
@@ -667,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);
}
/*
@@ -687,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)
{
@@ -796,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;
@@ -1267,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;
}
@@ -1393,7 +1376,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
case trunc_none:
break;
}
- strbuf_addstr(sb, local_sb.buf);
+ strbuf_addbuf(sb, &local_sb);
} else {
int sb_len = sb->len, offset = 0;
if (c->flush_type == flush_left)
@@ -1536,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,
@@ -1573,12 +1554,7 @@ static void pp_header(struct pretty_print_context *pp,
}
if (!parents_shown) {
- struct commit_list *parent;
- int num;
- for (parent = commit->parents, num = 0;
- parent;
- parent = parent->next, num++)
- ;
+ unsigned num = commit_list_count(commit->parents);
/* with enough slop */
strbuf_grow(sb, num * 50 + 20);
add_merge_info(pp, sb, commit);
@@ -1705,7 +1681,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;
@@ -1772,7 +1748,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,
diff --git a/prio-queue.c b/prio-queue.c
index c9f8c6d253..e4365b00d6 100644
--- a/prio-queue.c
+++ b/prio-queue.c
@@ -1,18 +1,30 @@
#include "cache.h"
-#include "commit.h"
#include "prio-queue.h"
+static inline int compare(struct prio_queue *queue, int i, int j)
+{
+ int cmp = queue->compare(queue->array[i].data, queue->array[j].data,
+ queue->cb_data);
+ if (!cmp)
+ cmp = queue->array[i].ctr - queue->array[j].ctr;
+ return cmp;
+}
+
+static inline void swap(struct prio_queue *queue, int i, int j)
+{
+ struct prio_queue_entry tmp = queue->array[i];
+ queue->array[i] = queue->array[j];
+ queue->array[j] = tmp;
+}
+
void prio_queue_reverse(struct prio_queue *queue)
{
int i, j;
if (queue->compare != NULL)
die("BUG: prio_queue_reverse() on non-LIFO queue");
- for (i = 0; i <= (j = (queue->nr - 1) - i); i++) {
- struct commit *swap = queue->array[i];
- queue->array[i] = queue->array[j];
- queue->array[j] = swap;
- }
+ for (i = 0; i <= (j = (queue->nr - 1) - i); i++)
+ swap(queue, i, j);
}
void clear_prio_queue(struct prio_queue *queue)
@@ -21,44 +33,42 @@ void clear_prio_queue(struct prio_queue *queue)
queue->nr = 0;
queue->alloc = 0;
queue->array = NULL;
+ queue->insertion_ctr = 0;
}
void prio_queue_put(struct prio_queue *queue, void *thing)
{
- prio_queue_compare_fn compare = queue->compare;
int ix, parent;
/* Append at the end */
ALLOC_GROW(queue->array, queue->nr + 1, queue->alloc);
- queue->array[queue->nr++] = thing;
- if (!compare)
+ queue->array[queue->nr].ctr = queue->insertion_ctr++;
+ queue->array[queue->nr].data = thing;
+ queue->nr++;
+ if (!queue->compare)
return; /* LIFO */
/* Bubble up the new one */
for (ix = queue->nr - 1; ix; ix = parent) {
parent = (ix - 1) / 2;
- if (compare(queue->array[parent], queue->array[ix],
- queue->cb_data) <= 0)
+ if (compare(queue, parent, ix) <= 0)
break;
- thing = queue->array[parent];
- queue->array[parent] = queue->array[ix];
- queue->array[ix] = thing;
+ swap(queue, parent, ix);
}
}
void *prio_queue_get(struct prio_queue *queue)
{
- void *result, *swap;
+ void *result;
int ix, child;
- prio_queue_compare_fn compare = queue->compare;
if (!queue->nr)
return NULL;
- if (!compare)
- return queue->array[--queue->nr]; /* LIFO */
+ if (!queue->compare)
+ return queue->array[--queue->nr].data; /* LIFO */
- result = queue->array[0];
+ result = queue->array[0].data;
if (!--queue->nr)
return result;
@@ -67,18 +77,14 @@ void *prio_queue_get(struct prio_queue *queue)
/* Push down the one at the root */
for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) {
child = ix * 2 + 1; /* left */
- if ((child + 1 < queue->nr) &&
- (compare(queue->array[child], queue->array[child + 1],
- queue->cb_data) >= 0))
+ if (child + 1 < queue->nr &&
+ compare(queue, child, child + 1) >= 0)
child++; /* use right child */
- if (compare(queue->array[ix], queue->array[child],
- queue->cb_data) <= 0)
+ if (compare(queue, ix, child) <= 0)
break;
- swap = queue->array[child];
- queue->array[child] = queue->array[ix];
- queue->array[ix] = swap;
+ swap(queue, child, ix);
}
return result;
}
diff --git a/prio-queue.h b/prio-queue.h
index 9c3cd1f875..d030ec9dd6 100644
--- a/prio-queue.h
+++ b/prio-queue.h
@@ -21,11 +21,17 @@
*/
typedef int (*prio_queue_compare_fn)(const void *one, const void *two, void *cb_data);
+struct prio_queue_entry {
+ unsigned ctr;
+ void *data;
+};
+
struct prio_queue {
prio_queue_compare_fn compare;
+ unsigned insertion_ctr;
void *cb_data;
int alloc, nr;
- void **array;
+ struct prio_queue_entry *array;
};
/*
diff --git a/progress.c b/progress.c
index 261314ef3c..412e6b1ecc 100644
--- a/progress.c
+++ b/progress.c
@@ -12,13 +12,14 @@
#include "gettext.h"
#include "progress.h"
#include "strbuf.h"
+#include "trace.h"
#define TP_IDX_MAX 8
struct throughput {
off_t curr_total;
off_t prev_total;
- struct timeval prev_tv;
+ uint64_t prev_ns;
unsigned int avg_bytes;
unsigned int avg_misecs;
unsigned int last_bytes[TP_IDX_MAX];
@@ -127,65 +128,65 @@ static void throughput_string(struct strbuf *buf, off_t total,
void display_throughput(struct progress *progress, off_t total)
{
struct throughput *tp;
- struct timeval tv;
- unsigned int misecs;
+ uint64_t now_ns;
+ unsigned int misecs, count, rate;
+ struct strbuf buf = STRBUF_INIT;
if (!progress)
return;
tp = progress->throughput;
- gettimeofday(&tv, NULL);
+ now_ns = getnanotime();
if (!tp) {
progress->throughput = tp = calloc(1, sizeof(*tp));
if (tp) {
tp->prev_total = tp->curr_total = total;
- tp->prev_tv = tv;
+ tp->prev_ns = now_ns;
}
return;
}
tp->curr_total = total;
+ /* only update throughput every 0.5 s */
+ if (now_ns - tp->prev_ns <= 500000000)
+ return;
+
/*
- * We have x = bytes and y = microsecs. We want z = KiB/s:
+ * We have x = bytes and y = nanosecs. We want z = KiB/s:
*
- * z = (x / 1024) / (y / 1000000)
- * z = x / y * 1000000 / 1024
- * z = x / (y * 1024 / 1000000)
+ * z = (x / 1024) / (y / 1000000000)
+ * z = x / y * 1000000000 / 1024
+ * z = x / (y * 1024 / 1000000000)
* z = x / y'
*
* To simplify things we'll keep track of misecs, or 1024th of a sec
* obtained with:
*
- * y' = y * 1024 / 1000000
- * y' = y / (1000000 / 1024)
- * y' = y / 977
+ * y' = y * 1024 / 1000000000
+ * y' = y * (2^10 / 2^42) * (2^42 / 1000000000)
+ * y' = y / 2^32 * 4398
+ * y' = (y * 4398) >> 32
*/
- misecs = (tv.tv_sec - tp->prev_tv.tv_sec) * 1024;
- misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977;
+ misecs = ((now_ns - tp->prev_ns) * 4398) >> 32;
- if (misecs > 512) {
- struct strbuf buf = STRBUF_INIT;
- unsigned int count, rate;
+ count = total - tp->prev_total;
+ tp->prev_total = total;
+ tp->prev_ns = now_ns;
+ tp->avg_bytes += count;
+ tp->avg_misecs += misecs;
+ rate = tp->avg_bytes / tp->avg_misecs;
+ tp->avg_bytes -= tp->last_bytes[tp->idx];
+ tp->avg_misecs -= tp->last_misecs[tp->idx];
+ tp->last_bytes[tp->idx] = count;
+ tp->last_misecs[tp->idx] = misecs;
+ tp->idx = (tp->idx + 1) % TP_IDX_MAX;
- count = total - tp->prev_total;
- tp->prev_total = total;
- tp->prev_tv = tv;
- tp->avg_bytes += count;
- tp->avg_misecs += misecs;
- rate = tp->avg_bytes / tp->avg_misecs;
- tp->avg_bytes -= tp->last_bytes[tp->idx];
- tp->avg_misecs -= tp->last_misecs[tp->idx];
- tp->last_bytes[tp->idx] = count;
- tp->last_misecs[tp->idx] = misecs;
- tp->idx = (tp->idx + 1) % TP_IDX_MAX;
-
- throughput_string(&buf, total, rate);
- strncpy(tp->display, buf.buf, sizeof(tp->display));
- strbuf_release(&buf);
- if (progress->last_value != -1 && progress_update)
- display(progress, progress->last_value, NULL);
- }
+ throughput_string(&buf, total, rate);
+ strncpy(tp->display, buf.buf, sizeof(tp->display));
+ strbuf_release(&buf);
+ if (progress->last_value != -1 && progress_update)
+ display(progress, progress->last_value, NULL);
}
int display_progress(struct progress *progress, unsigned n)
diff --git a/read-cache.c b/read-cache.c
index 7f5645e745..5d3c8bd4aa 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -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 @@ unmap:
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 dc457742ea..84b9070b8d 100644
--- 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 @@ out:
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
@@ -1334,6 +1533,7 @@ static const char *handle_missing_loose_ref(const char *refname,
}
}
+/* This function needs to return a meaningful errno on failure */
const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
{
int depth = MAXDEPTH;
@@ -1344,8 +1544,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
if (flag)
*flag = 0;
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ errno = EINVAL;
return NULL;
+ }
for (;;) {
char path[PATH_MAX];
@@ -1353,8 +1555,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
char *buf;
int fd;
- if (--depth < 0)
+ if (--depth < 0) {
+ errno = ELOOP;
return NULL;
+ }
git_snpath(path, sizeof(path), "%s", refname);
@@ -1416,9 +1620,13 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
return NULL;
}
len = read_in_full(fd, buffer, sizeof(buffer)-1);
- close(fd);
- if (len < 0)
+ if (len < 0) {
+ int save_errno = errno;
+ close(fd);
+ errno = save_errno;
return NULL;
+ }
+ close(fd);
while (len && isspace(buffer[len-1]))
len--;
buffer[len] = '\0';
@@ -1435,6 +1643,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
(buffer[40] != '\0' && !isspace(buffer[40]))) {
if (flag)
*flag |= REF_ISBROKEN;
+ errno = EINVAL;
return NULL;
}
return refname;
@@ -1447,6 +1656,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
if (flag)
*flag |= REF_ISBROKEN;
+ errno = EINVAL;
return NULL;
}
refname = strcpy(refname_buffer, buf);
@@ -1531,9 +1741,8 @@ static enum peel_status peel_object(const unsigned char *name, unsigned char *sh
if (o->type == OBJ_NONE) {
int type = sha1_object_info(name, NULL);
- if (type < 0)
+ if (type < 0 || !object_as_type(o, type, 0))
return PEEL_INVALID;
- o->type = type;
}
if (o->type != OBJ_TAG)
@@ -1932,18 +2141,22 @@ int refname_match(const char *abbrev_name, const char *full_name)
return 0;
}
+/* This function should make sure errno is meaningful on error */
static struct ref_lock *verify_lock(struct ref_lock *lock,
const unsigned char *old_sha1, int mustexist)
{
if (read_ref_full(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+ int save_errno = errno;
error("Can't verify ref %s", lock->ref_name);
unlock_ref(lock);
+ errno = save_errno;
return NULL;
}
if (hashcmp(lock->old_sha1, old_sha1)) {
error("Ref %s is at %s but expected %s", lock->ref_name,
sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
unlock_ref(lock);
+ errno = EBUSY;
return NULL;
}
return lock;
@@ -1956,14 +2169,16 @@ static int remove_empty_directories(const char *file)
* only empty directories), remove them.
*/
struct strbuf path;
- int result;
+ int result, save_errno;
strbuf_init(&path, 20);
strbuf_addstr(&path, file);
result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
+ save_errno = errno;
strbuf_release(&path);
+ errno = save_errno;
return result;
}
@@ -2052,6 +2267,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
return logs_found;
}
+/* This function should make sure errno is meaningful on error */
static struct ref_lock *lock_ref_sha1_basic(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p)
@@ -2212,6 +2428,7 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
return 0;
}
+/* This should return a meaningful errno on failure */
int lock_packed_refs(int flags)
{
struct packed_ref_cache *packed_ref_cache;
@@ -2231,11 +2448,16 @@ int lock_packed_refs(int flags)
return 0;
}
+/*
+ * Commit the packed refs changes.
+ * On error we must make sure that errno contains a meaningful value.
+ */
int commit_packed_refs(void)
{
struct packed_ref_cache *packed_ref_cache =
get_packed_ref_cache(&ref_cache);
int error = 0;
+ int save_errno = 0;
if (!packed_ref_cache->lock)
die("internal error: packed-refs not locked");
@@ -2245,10 +2467,13 @@ int commit_packed_refs(void)
do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
0, write_packed_entry_fn,
&packed_ref_cache->lock->fd);
- if (commit_lock_file(packed_ref_cache->lock))
+ if (commit_lock_file(packed_ref_cache->lock)) {
+ save_errno = errno;
error = -1;
+ }
packed_ref_cache->lock = NULL;
release_packed_ref_cache(packed_ref_cache);
+ errno = save_errno;
return error;
}
@@ -2455,12 +2680,12 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
return 0;
}
-int repack_without_refs(const char **refnames, int n)
+int repack_without_refs(const char **refnames, int n, struct strbuf *err)
{
struct ref_dir *packed;
struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
struct string_list_item *ref_to_delete;
- int i, removed = 0;
+ int i, ret, removed = 0;
/* Look for a packed ref */
for (i = 0; i < n; i++)
@@ -2472,6 +2697,11 @@ int repack_without_refs(const char **refnames, int n)
return 0; /* no refname exists in packed refs */
if (lock_packed_refs(0)) {
+ if (err) {
+ unable_to_lock_message(git_path("packed-refs"), errno,
+ err);
+ return -1;
+ }
unable_to_lock_error(git_path("packed-refs"), errno);
return error("cannot delete '%s' from packed refs", refnames[i]);
}
@@ -2498,12 +2728,16 @@ int repack_without_refs(const char **refnames, int n)
}
/* Write what remains */
- return commit_packed_refs();
+ ret = commit_packed_refs();
+ if (ret && err)
+ strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
+ strerror(errno));
+ return ret;
}
static int repack_without_ref(const char *refname)
{
- return repack_without_refs(&refname, 1);
+ return repack_without_refs(&refname, 1, NULL);
}
static int delete_ref_loose(struct ref_lock *lock, int flag)
@@ -2741,6 +2975,7 @@ static int copy_msg(char *buf, const char *msg)
return cp - buf;
}
+/* This function must set a meaningful errno on failure */
int log_ref_setup(const char *refname, char *logfile, int bufsize)
{
int logfd, oflags = O_APPEND | O_WRONLY;
@@ -2751,9 +2986,12 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)
starts_with(refname, "refs/remotes/") ||
starts_with(refname, "refs/notes/") ||
!strcmp(refname, "HEAD"))) {
- if (safe_create_leading_directories(logfile) < 0)
- return error("unable to create directory for %s",
- logfile);
+ if (safe_create_leading_directories(logfile) < 0) {
+ int save_errno = errno;
+ error("unable to create directory for %s", logfile);
+ errno = save_errno;
+ return -1;
+ }
oflags |= O_CREAT;
}
@@ -2764,15 +3002,22 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)
if ((oflags & O_CREAT) && errno == EISDIR) {
if (remove_empty_directories(logfile)) {
- return error("There are still logs under '%s'",
- logfile);
+ int save_errno = errno;
+ error("There are still logs under '%s'",
+ logfile);
+ errno = save_errno;
+ return -1;
}
logfd = open(logfile, oflags, 0666);
}
- if (logfd < 0)
- return error("Unable to append to %s: %s",
- logfile, strerror(errno));
+ if (logfd < 0) {
+ int save_errno = errno;
+ error("Unable to append to %s: %s", logfile,
+ strerror(errno));
+ errno = save_errno;
+ return -1;
+ }
}
adjust_shared_perm(logfile);
@@ -2812,24 +3057,38 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
len += copy_msg(logrec + len - 1, msg) - 1;
written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
free(logrec);
- if (close(logfd) != 0 || written != len)
- return error("Unable to append to %s", log_file);
+ if (written != len) {
+ int save_errno = errno;
+ close(logfd);
+ error("Unable to append to %s", log_file);
+ errno = save_errno;
+ return -1;
+ }
+ if (close(logfd)) {
+ int save_errno = errno;
+ error("Unable to append to %s", log_file);
+ errno = save_errno;
+ return -1;
+ }
return 0;
}
-static int is_branch(const char *refname)
+int is_branch(const char *refname)
{
return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
}
+/* This function must return a meaningful errno */
int write_ref_sha1(struct ref_lock *lock,
const unsigned char *sha1, const char *logmsg)
{
static char term = '\n';
struct object *o;
- if (!lock)
+ if (!lock) {
+ errno = EINVAL;
return -1;
+ }
if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) {
unlock_ref(lock);
return 0;
@@ -2839,19 +3098,23 @@ int write_ref_sha1(struct ref_lock *lock,
error("Trying to write ref %s with nonexistent object %s",
lock->ref_name, sha1_to_hex(sha1));
unlock_ref(lock);
+ errno = EINVAL;
return -1;
}
if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
error("Trying to write non-commit object %s to branch %s",
sha1_to_hex(sha1), lock->ref_name);
unlock_ref(lock);
+ errno = EINVAL;
return -1;
}
if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
- write_in_full(lock->lock_fd, &term, 1) != 1
- || close_ref(lock) < 0) {
+ write_in_full(lock->lock_fd, &term, 1) != 1 ||
+ close_ref(lock) < 0) {
+ int save_errno = errno;
error("Couldn't write %s", lock->lk->filename);
unlock_ref(lock);
+ errno = save_errno;
return -1;
}
clear_loose_ref_cache(&ref_cache);
@@ -3233,7 +3496,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) {
@@ -3288,10 +3551,13 @@ static struct ref_lock *update_ref_lock(const char *refname,
static int update_ref_write(const char *action, const char *refname,
const unsigned char *sha1, struct ref_lock *lock,
- enum action_on_err onerr)
+ struct strbuf *err, enum action_on_err onerr)
{
if (write_ref_sha1(lock, sha1, action) < 0) {
const char *str = "Cannot update the ref '%s'.";
+ if (err)
+ strbuf_addf(err, str, refname);
+
switch (onerr) {
case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
@@ -3334,10 +3600,13 @@ struct ref_transaction *ref_transaction_begin(void)
return xcalloc(1, sizeof(struct ref_transaction));
}
-static void ref_transaction_free(struct ref_transaction *transaction)
+void ref_transaction_free(struct ref_transaction *transaction)
{
int i;
+ if (!transaction)
+ return;
+
for (i = 0; i < transaction->nr; i++)
free(transaction->updates[i]);
@@ -3345,11 +3614,6 @@ static void ref_transaction_free(struct ref_transaction *transaction)
free(transaction);
}
-void ref_transaction_rollback(struct ref_transaction *transaction)
-{
- ref_transaction_free(transaction);
-}
-
static struct ref_update *add_update(struct ref_transaction *transaction,
const char *refname)
{
@@ -3362,23 +3626,30 @@ static struct ref_update *add_update(struct ref_transaction *transaction,
return update;
}
-void ref_transaction_update(struct ref_transaction *transaction,
- const char *refname,
- unsigned char *new_sha1, unsigned char *old_sha1,
- int flags, int have_old)
+int ref_transaction_update(struct ref_transaction *transaction,
+ const char *refname,
+ const unsigned char *new_sha1,
+ const unsigned char *old_sha1,
+ int flags, int have_old,
+ struct strbuf *err)
{
- struct ref_update *update = add_update(transaction, refname);
+ struct ref_update *update;
+ if (have_old && !old_sha1)
+ die("BUG: have_old is true but old_sha1 is NULL");
+
+ update = add_update(transaction, refname);
hashcpy(update->new_sha1, new_sha1);
update->flags = flags;
update->have_old = have_old;
if (have_old)
hashcpy(update->old_sha1, old_sha1);
+ return 0;
}
void ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
- unsigned char *new_sha1,
+ const unsigned char *new_sha1,
int flags)
{
struct ref_update *update = add_update(transaction, refname);
@@ -3392,7 +3663,7 @@ void ref_transaction_create(struct ref_transaction *transaction,
void ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
- unsigned char *old_sha1,
+ const unsigned char *old_sha1,
int flags, int have_old)
{
struct ref_update *update = add_update(transaction, refname);
@@ -3413,7 +3684,7 @@ int update_ref(const char *action, const char *refname,
lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
if (!lock)
return 1;
- return update_ref_write(action, refname, sha1, lock, onerr);
+ return update_ref_write(action, refname, sha1, lock, NULL, onerr);
}
static int ref_update_compare(const void *r1, const void *r2)
@@ -3424,28 +3695,23 @@ static int ref_update_compare(const void *r1, const void *r2)
}
static int ref_update_reject_duplicates(struct ref_update **updates, int n,
- enum action_on_err onerr)
+ struct strbuf *err)
{
int i;
for (i = 1; i < n; i++)
if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
const char *str =
"Multiple updates for ref '%s' not allowed.";
- switch (onerr) {
- case UPDATE_REFS_MSG_ON_ERR:
- error(str, updates[i]->refname); break;
- case UPDATE_REFS_DIE_ON_ERR:
- die(str, updates[i]->refname); break;
- case UPDATE_REFS_QUIET_ON_ERR:
- break;
- }
+ if (err)
+ strbuf_addf(err, str, updates[i]->refname);
+
return 1;
}
return 0;
}
int ref_transaction_commit(struct ref_transaction *transaction,
- const char *msg, enum action_on_err onerr)
+ const char *msg, struct strbuf *err)
{
int ret = 0, delnum = 0, i;
const char **delnames;
@@ -3460,7 +3726,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
/* Copy, sort, and reject duplicate refs */
qsort(updates, n, sizeof(*updates), ref_update_compare);
- ret = ref_update_reject_duplicates(updates, n, onerr);
+ ret = ref_update_reject_duplicates(updates, n, err);
if (ret)
goto cleanup;
@@ -3472,8 +3738,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
(update->have_old ?
update->old_sha1 : NULL),
update->flags,
- &update->type, onerr);
+ &update->type,
+ UPDATE_REFS_QUIET_ON_ERR);
if (!update->lock) {
+ if (err)
+ strbuf_addf(err, "Cannot lock the ref '%s'.",
+ update->refname);
ret = 1;
goto cleanup;
}
@@ -3487,7 +3757,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
ret = update_ref_write(msg,
update->refname,
update->new_sha1,
- update->lock, onerr);
+ update->lock, err,
+ UPDATE_REFS_QUIET_ON_ERR);
update->lock = NULL; /* freed by update_ref_write */
if (ret)
goto cleanup;
@@ -3504,7 +3775,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
}
}
- ret |= repack_without_refs(delnames, delnum);
+ ret |= repack_without_refs(delnames, delnum, err);
for (i = 0; i < delnum; i++)
unlink_or_warn(git_path("logs/%s", delnames[i]));
clear_loose_ref_cache(&ref_cache);
@@ -3514,7 +3785,6 @@ cleanup:
if (updates[i]->lock)
unlock_ref(updates[i]->lock);
free(delnames);
- ref_transaction_free(transaction);
return ret;
}
diff --git a/refs.h b/refs.h
index 4e3050d976..ec46acdde7 100644
--- a/refs.h
+++ b/refs.h
@@ -82,6 +82,7 @@ extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct st
/*
* Lock the packed-refs file for writing. Flags is passed to
* hold_lock_file_for_update(). Return 0 on success.
+ * Errno is set to something meaningful on error.
*/
extern int lock_packed_refs(int flags);
@@ -97,6 +98,7 @@ extern void add_packed_ref(const char *refname, const unsigned char *sha1);
* Write the current version of the packed refs cache from memory to
* disk. The packed-refs file must already be locked for writing (see
* lock_packed_refs()). Return zero on success.
+ * Sets errno to something meaningful on error.
*/
extern int commit_packed_refs(void);
@@ -121,10 +123,13 @@ extern void rollback_packed_refs(void);
*/
int pack_refs(unsigned int flags);
-extern int repack_without_refs(const char **refnames, int n);
+extern int repack_without_refs(const char **refnames, int n,
+ struct strbuf *err);
extern int ref_exists(const char *);
+extern int is_branch(const char *refname);
+
/*
* If refname is a non-symbolic reference that refers to a tag object,
* and the tag can be (recursively) dereferenced to a non-tag object,
@@ -135,11 +140,15 @@ extern int ref_exists(const char *);
*/
extern int peel_ref(const char *refname, unsigned char *sha1);
-/** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
+/*
+ * Locks a "refs/" ref returning the lock on success and NULL on failure.
+ * On failure errno is set to something meaningful.
+ */
extern struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1);
/** Locks any ref (for 'HEAD' type refs). */
#define REF_NODEREF 0x01
+/* errno is set to something meaningful on failure */
extern struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p);
@@ -156,7 +165,9 @@ extern void unlock_ref(struct ref_lock *lock);
/** Writes sha1 into the ref specified by the lock. **/
extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
-/** Setup reflog before using. **/
+/*
+ * Setup reflog before using. Set errno to something meaningful on failure.
+ */
int log_ref_setup(const char *refname, char *logfile, int bufsize);
/** Reads log for the value of ref during at_time. **/
@@ -219,18 +230,11 @@ enum action_on_err {
/*
* Begin a reference transaction. The reference transaction must
- * eventually be commited using ref_transaction_commit() or rolled
- * back using ref_transaction_rollback().
+ * be freed by calling ref_transaction_free().
*/
struct ref_transaction *ref_transaction_begin(void);
/*
- * Roll back a ref_transaction and free all associated data.
- */
-void ref_transaction_rollback(struct ref_transaction *transaction);
-
-
-/*
* The following functions add a reference check or update to a
* ref_transaction. In all of them, refname is the name of the
* reference to be affected. The functions make internal copies of
@@ -238,18 +242,22 @@ void ref_transaction_rollback(struct ref_transaction *transaction);
* can be REF_NODEREF; it is passed to update_ref_lock().
*/
-
/*
* Add a reference update to transaction. new_sha1 is the value that
* the reference should have after the update, or zeros if it should
* be deleted. If have_old is true, then old_sha1 holds the value
* that the reference should have had before the update, or zeros if
* it must not have existed beforehand.
+ * Function returns 0 on success and non-zero on failure. A failure to update
+ * means that the transaction as a whole has failed and will need to be
+ * rolled back. On failure the err buffer will be updated.
*/
-void ref_transaction_update(struct ref_transaction *transaction,
- const char *refname,
- unsigned char *new_sha1, unsigned char *old_sha1,
- int flags, int have_old);
+int ref_transaction_update(struct ref_transaction *transaction,
+ const char *refname,
+ const unsigned char *new_sha1,
+ const unsigned char *old_sha1,
+ int flags, int have_old,
+ struct strbuf *err);
/*
* Add a reference creation to transaction. new_sha1 is the value
@@ -259,7 +267,7 @@ void ref_transaction_update(struct ref_transaction *transaction,
*/
void ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
- unsigned char *new_sha1,
+ const unsigned char *new_sha1,
int flags);
/*
@@ -269,16 +277,23 @@ void ref_transaction_create(struct ref_transaction *transaction,
*/
void ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
- unsigned char *old_sha1,
+ const unsigned char *old_sha1,
int flags, int have_old);
/*
* Commit all of the changes that have been queued in transaction, as
* atomically as possible. Return a nonzero value if there is a
- * problem. The ref_transaction is freed by this function.
+ * problem.
+ * If err is non-NULL we will add an error string to it to explain why
+ * the transaction failed. The string does not end in newline.
*/
int ref_transaction_commit(struct ref_transaction *transaction,
- const char *msg, enum action_on_err onerr);
+ const char *msg, struct strbuf *err);
+
+/*
+ * Free an existing transaction and all associated data.
+ */
+void ref_transaction_free(struct ref_transaction *transaction);
/** Lock a ref and then write its file */
int update_ref(const char *action, const char *refname,
diff --git a/remote-curl.c b/remote-curl.c
index 4493b389de..0fcf2ce5ff 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -399,7 +399,7 @@ static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
rpc->pos = 0;
return CURLIOE_OK;
}
- fprintf(stderr, "Unable to rewind rpc post data - try increasing http.postBuffer\n");
+ error("unable to rewind rpc post data - try increasing http.postBuffer");
return CURLIOE_FAILRESTART;
default:
@@ -709,7 +709,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
free(targets[i]);
free(targets);
- return ret ? error("Fetch failed.") : 0;
+ return ret ? error("fetch failed.") : 0;
}
static int fetch_git(struct discovery *heads,
@@ -791,9 +791,9 @@ static void parse_fetch(struct strbuf *buf)
int alloc_heads = 0, nr_heads = 0;
do {
- if (starts_with(buf->buf, "fetch ")) {
- char *p = buf->buf + strlen("fetch ");
- char *name;
+ const char *p;
+ if (skip_prefix(buf->buf, "fetch ", &p)) {
+ const char *name;
struct ref *ref;
unsigned char old_sha1[20];
@@ -949,7 +949,7 @@ int main(int argc, const char **argv)
git_extract_argv0_path(argv[0]);
setup_git_directory_gently(&nongit);
if (argc < 2) {
- fprintf(stderr, "Remote needed\n");
+ error("remote-curl: usage: git remote-curl <remote> [<url>]");
return 1;
}
@@ -968,18 +968,18 @@ int main(int argc, const char **argv)
http_init(remote, url.buf, 0);
do {
+ const char *arg;
+
if (strbuf_getline(&buf, stdin, '\n') == EOF) {
if (ferror(stdin))
- fprintf(stderr, "Error reading command stream\n");
- else
- fprintf(stderr, "Unexpected end of command stream\n");
+ error("remote-curl: error reading command stream from git");
return 1;
}
if (buf.len == 0)
break;
if (starts_with(buf.buf, "fetch ")) {
if (nongit)
- die("Fetch attempted without a local repo");
+ die("remote-curl: fetch attempted without a local repo");
parse_fetch(&buf);
} else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
@@ -989,9 +989,8 @@ int main(int argc, const char **argv)
} else if (starts_with(buf.buf, "push ")) {
parse_push(&buf);
- } else if (starts_with(buf.buf, "option ")) {
- char *name = buf.buf + strlen("option ");
- char *value = strchr(name, ' ');
+ } else if (skip_prefix(buf.buf, "option ", &arg)) {
+ char *value = strchr(arg, ' ');
int result;
if (value)
@@ -999,7 +998,7 @@ int main(int argc, const char **argv)
else
value = "true";
- result = set_option(name, value);
+ result = set_option(arg, value);
if (!result)
printf("ok\n");
else if (result < 0)
@@ -1016,7 +1015,7 @@ int main(int argc, const char **argv)
printf("\n");
fflush(stdout);
} else {
- fprintf(stderr, "Unknown command '%s'\n", buf.buf);
+ error("remote-curl: unknown command '%s' from git", buf.buf);
return 1;
}
strbuf_reset(&buf);
diff --git a/remote-testsvn.c b/remote-testsvn.c
index 6be55cbe9e..686e07d317 100644
--- a/remote-testsvn.c
+++ b/remote-testsvn.c
@@ -175,8 +175,8 @@ static int cmd_import(const char *line)
char *note_msg;
unsigned char head_sha1[20];
unsigned int startrev;
- struct argv_array svndump_argv = ARGV_ARRAY_INIT;
struct child_process svndump_proc;
+ const char *command = "svnrdump";
if (read_ref(private_ref, head_sha1))
startrev = 0;
@@ -202,15 +202,14 @@ static int cmd_import(const char *line)
} else {
memset(&svndump_proc, 0, sizeof(struct child_process));
svndump_proc.out = -1;
- argv_array_push(&svndump_argv, "svnrdump");
- argv_array_push(&svndump_argv, "dump");
- argv_array_push(&svndump_argv, url);
- argv_array_pushf(&svndump_argv, "-r%u:HEAD", startrev);
- svndump_proc.argv = svndump_argv.argv;
+ argv_array_push(&svndump_proc.args, command);
+ argv_array_push(&svndump_proc.args, "dump");
+ argv_array_push(&svndump_proc.args, url);
+ argv_array_pushf(&svndump_proc.args, "-r%u:HEAD", startrev);
code = start_command(&svndump_proc);
if (code)
- die("Unable to start %s, code %d", svndump_proc.argv[0], code);
+ die("Unable to start %s, code %d", command, code);
dumpin_fd = svndump_proc.out;
}
/* setup marks file import/export */
@@ -226,8 +225,7 @@ static int cmd_import(const char *line)
if (!dump_from_file) {
code = finish_command(&svndump_proc);
if (code)
- warning("%s, returned %d", svndump_proc.argv[0], code);
- argv_array_clear(&svndump_argv);
+ warning("%s, returned %d", command, code);
}
return 0;
diff --git a/remote.c b/remote.c
index ae04043203..3d6c86a36f 100644
--- 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;
}
@@ -488,9 +484,8 @@ static void read_config(void)
current_branch = NULL;
head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
if (head_ref && (flag & REF_ISSYMREF) &&
- starts_with(head_ref, "refs/heads/")) {
- current_branch =
- make_branch(head_ref + strlen("refs/heads/"), 0);
+ skip_prefix(head_ref, "refs/heads/", &head_ref)) {
+ current_branch = make_branch(head_ref, 0);
}
git_config(handle_config, NULL);
if (branch_pushremote_name) {
diff --git a/rerere.c b/rerere.c
index d55aa8a01b..d84b495895 100644
--- a/rerere.c
+++ b/rerere.c
@@ -207,11 +207,11 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
strbuf_reset(&one);
strbuf_reset(&two);
} else if (hunk == RR_SIDE_1)
- strbuf_addstr(&one, buf.buf);
+ strbuf_addbuf(&one, &buf);
else if (hunk == RR_ORIGINAL)
; /* discard */
else if (hunk == RR_SIDE_2)
- strbuf_addstr(&two, buf.buf);
+ strbuf_addbuf(&two, &buf);
else
rerere_io_putstr(buf.buf, io);
continue;
@@ -492,8 +492,7 @@ static int update_paths(struct string_list *update)
}
if (!status && active_cache_changed) {
- if (write_cache(fd, active_cache, active_nr) ||
- commit_locked_index(&index_lock))
+ if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
die("Unable to write new index file");
} else if (fd >= 0)
rollback_lock_file(&index_lock);
diff --git a/resolve-undo.c b/resolve-undo.c
index 44c697c36d..468a2eb92c 100644
--- a/resolve-undo.c
+++ b/resolve-undo.c
@@ -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)
diff --git a/revision.c b/revision.c
index 8351e794df..2571ada6bf 100644
--- a/revision.c
+++ b/revision.c
@@ -2791,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)
@@ -2833,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;
}
diff --git a/run-command.c b/run-command.c
index be07d4ad33..576dfeaa3f 100644
--- a/run-command.c
+++ b/run-command.c
@@ -770,28 +770,21 @@ char *find_hook(const char *name)
int run_hook_ve(const char *const *env, const char *name, va_list args)
{
struct child_process hook;
- struct argv_array argv = ARGV_ARRAY_INIT;
const char *p;
- int ret;
p = find_hook(name);
if (!p)
return 0;
- argv_array_push(&argv, p);
-
- while ((p = va_arg(args, const char *)))
- argv_array_push(&argv, p);
-
memset(&hook, 0, sizeof(hook));
- hook.argv = argv.argv;
+ argv_array_push(&hook.args, p);
+ while ((p = va_arg(args, const char *)))
+ argv_array_push(&hook.args, p);
hook.env = env;
hook.no_stdin = 1;
hook.stdout_to_stderr = 1;
- ret = run_command(&hook);
- argv_array_clear(&argv);
- return ret;
+ return run_command(&hook);
}
int run_hook_le(const char *const *env, const char *name, ...)
diff --git a/sequencer.c b/sequencer.c
index 0a80c58d11..3c060e0547 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -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;
}
diff --git a/sh-i18n--envsubst.c b/sh-i18n--envsubst.c
index 855d28cf94..6dd03a974a 100644
--- a/sh-i18n--envsubst.c
+++ b/sh-i18n--envsubst.c
@@ -278,9 +278,7 @@ static string_list_ty variables_set;
static void
note_variable (const char *var_ptr, size_t var_len)
{
- char *string = xmalloc (var_len + 1);
- memcpy (string, var_ptr, var_len);
- string[var_len] = '\0';
+ char *string = xmemdupz (var_ptr, var_len);
string_list_append (&variables_set, string);
}
diff --git a/sha1_file.c b/sha1_file.c
index 34d527f670..3f70b1d86a 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -36,9 +36,6 @@ static inline uintmax_t sz_fmt(size_t s) { return s; }
const unsigned char null_sha1[20];
-static const char *no_log_pack_access = "no_log_pack_access";
-static const char *log_pack_access;
-
/*
* This is meant to hold a *small* number of objects that you would
* want read_sha1_file() to be able to return, but yet you do not want
@@ -268,9 +265,9 @@ static struct alternate_object_database **alt_odb_tail;
* SHA1, an extra slash for the first level indirection, and the
* terminating NUL.
*/
-static int link_alt_odb_entry(const char *entry, const char *relative_base, int depth)
+static int link_alt_odb_entry(const char *entry, const char *relative_base,
+ int depth, const char *normalized_objdir)
{
- const char *objdir = get_object_directory();
struct alternate_object_database *ent;
struct alternate_object_database *alt;
int pfxlen, entlen;
@@ -315,12 +312,13 @@ 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;
}
}
- if (!strcmp(ent->base, objdir)) {
+ if (!strcmp_icase(ent->base, normalized_objdir)) {
free(ent);
return -1;
}
@@ -344,6 +342,7 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
struct string_list entries = STRING_LIST_INIT_NODUP;
char *alt_copy;
int i;
+ struct strbuf objdirbuf = STRBUF_INIT;
if (depth > 5) {
error("%s: ignoring alternate object stores, nesting too deep.",
@@ -351,6 +350,9 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
return;
}
+ strbuf_addstr(&objdirbuf, absolute_path(get_object_directory()));
+ normalize_path_copy(objdirbuf.buf, objdirbuf.buf);
+
alt_copy = xmemdupz(alt, len);
string_list_split_in_place(&entries, alt_copy, sep, -1);
for (i = 0; i < entries.nr; i++) {
@@ -361,11 +363,12 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
error("%s: ignoring relative alternate object store %s",
relative_base, entry);
} else {
- link_alt_odb_entry(entry, relative_base, depth);
+ link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf);
}
}
string_list_clear(&entries, 0);
free(alt_copy);
+ strbuf_release(&objdirbuf);
}
void read_info_alternates(const char * relative_base, int depth)
@@ -1177,48 +1180,42 @@ static void report_pack_garbage(struct string_list *list)
static void prepare_packed_git_one(char *objdir, int local)
{
- /* Ensure that this buffer is large enough so that we can
- append "/pack/" without clobbering the stack even if
- strlen(objdir) were PATH_MAX. */
- char path[PATH_MAX + 1 + 4 + 1 + 1];
- int len;
+ struct strbuf path = STRBUF_INIT;
+ size_t dirnamelen;
DIR *dir;
struct dirent *de;
struct string_list garbage = STRING_LIST_INIT_DUP;
- sprintf(path, "%s/pack", objdir);
- len = strlen(path);
- dir = opendir(path);
+ strbuf_addstr(&path, objdir);
+ strbuf_addstr(&path, "/pack");
+ dir = opendir(path.buf);
if (!dir) {
if (errno != ENOENT)
error("unable to open object pack directory: %s: %s",
- path, strerror(errno));
+ path.buf, strerror(errno));
+ strbuf_release(&path);
return;
}
- path[len++] = '/';
+ strbuf_addch(&path, '/');
+ dirnamelen = path.len;
while ((de = readdir(dir)) != NULL) {
- int namelen = strlen(de->d_name);
struct packed_git *p;
-
- if (len + namelen + 1 > sizeof(path)) {
- if (report_garbage) {
- struct strbuf sb = STRBUF_INIT;
- strbuf_addf(&sb, "%.*s/%s", len - 1, path, de->d_name);
- report_garbage("path too long", sb.buf);
- strbuf_release(&sb);
- }
- continue;
- }
+ size_t base_len;
if (is_dot_or_dotdot(de->d_name))
continue;
- strcpy(path + len, de->d_name);
+ strbuf_setlen(&path, dirnamelen);
+ strbuf_addstr(&path, de->d_name);
- if (has_extension(de->d_name, ".idx")) {
+ base_len = path.len;
+ if (strip_suffix_mem(path.buf, &base_len, ".idx")) {
/* Don't reopen a pack we already have. */
for (p = packed_git; p; p = p->next) {
- if (!memcmp(path, p->pack_name, len + namelen - 4))
+ size_t len;
+ if (strip_suffix(p->pack_name, ".pack", &len) &&
+ len == base_len &&
+ !memcmp(p->pack_name, path.buf, len))
break;
}
if (p == NULL &&
@@ -1226,24 +1223,25 @@ static void prepare_packed_git_one(char *objdir, int local)
* See if it really is a valid .idx file with
* corresponding .pack file that we can map.
*/
- (p = add_packed_git(path, len + namelen, local)) != NULL)
+ (p = add_packed_git(path.buf, path.len, local)) != NULL)
install_packed_git(p);
}
if (!report_garbage)
continue;
- if (has_extension(de->d_name, ".idx") ||
- has_extension(de->d_name, ".pack") ||
- has_extension(de->d_name, ".bitmap") ||
- has_extension(de->d_name, ".keep"))
- string_list_append(&garbage, path);
+ if (ends_with(de->d_name, ".idx") ||
+ ends_with(de->d_name, ".pack") ||
+ ends_with(de->d_name, ".bitmap") ||
+ ends_with(de->d_name, ".keep"))
+ string_list_append(&garbage, path.buf);
else
- report_garbage("garbage found", path);
+ report_garbage("garbage found", path.buf);
}
closedir(dir);
report_pack_garbage(&garbage);
string_list_clear(&garbage, 0);
+ strbuf_release(&path);
}
static int sort_pack(const void *a_, const void *b_)
@@ -2085,27 +2083,9 @@ static void *read_object(const unsigned char *sha1, enum object_type *type,
static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
{
- static FILE *log_file;
-
- if (!log_pack_access)
- log_pack_access = getenv("GIT_TRACE_PACK_ACCESS");
- if (!log_pack_access)
- log_pack_access = no_log_pack_access;
- if (log_pack_access == no_log_pack_access)
- return;
-
- if (!log_file) {
- log_file = fopen(log_pack_access, "w");
- if (!log_file) {
- error("cannot open pack access log '%s' for writing: %s",
- log_pack_access, strerror(errno));
- log_pack_access = no_log_pack_access;
- return;
- }
- }
- fprintf(log_file, "%s %"PRIuMAX"\n",
- p->pack_name, (uintmax_t)obj_offset);
- fflush(log_file);
+ static struct trace_key pack_access = TRACE_KEY_INIT(PACK_ACCESS);
+ trace_printf_key(&pack_access, "%s %"PRIuMAX"\n",
+ p->pack_name, (uintmax_t)obj_offset);
}
int do_check_packed_object_crc;
@@ -2130,8 +2110,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
int base_from_cache = 0;
- if (log_pack_access != no_log_pack_access)
- write_pack_access_log(p, obj_offset);
+ write_pack_access_log(p, obj_offset);
/* PHASE 1: drill down to the innermost base object */
for (;;) {
diff --git a/sha1_name.c b/sha1_name.c
index 2b6322fad0..6ccd3a53f8 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -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);
@@ -911,10 +901,8 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
const char *match = NULL, *target = NULL;
size_t len;
- if (starts_with(message, "checkout: moving from ")) {
- match = message + strlen("checkout: moving from ");
+ if (skip_prefix(message, "checkout: moving from ", &match))
target = strstr(match, " to ");
- }
if (!match || !target)
return 0;
@@ -958,7 +946,7 @@ static int interpret_nth_prior_checkout(const char *name, int namelen,
retval = 0;
if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) {
strbuf_reset(buf);
- strbuf_add(buf, cb.buf.buf, cb.buf.len);
+ strbuf_addbuf(buf, &cb.buf);
retval = brace - name + 1;
}
@@ -1252,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/shallow.c b/shallow.c
index 0b267b6411..de07709e33 100644
--- a/shallow.c
+++ b/shallow.c
@@ -325,7 +325,7 @@ void prune_shallow(int show_only)
strbuf_release(&sb);
}
-#define TRACE_KEY "GIT_TRACE_SHALLOW"
+struct trace_key trace_shallow = TRACE_KEY_INIT(SHALLOW);
/*
* Step 1, split sender shallow commits into "ours" and "theirs"
@@ -334,7 +334,7 @@ void prune_shallow(int show_only)
void prepare_shallow_info(struct shallow_info *info, struct sha1_array *sa)
{
int i;
- trace_printf_key(TRACE_KEY, "shallow: prepare_shallow_info\n");
+ trace_printf_key(&trace_shallow, "shallow: prepare_shallow_info\n");
memset(info, 0, sizeof(*info));
info->shallow = sa;
if (!sa)
@@ -365,7 +365,7 @@ void remove_nonexistent_theirs_shallow(struct shallow_info *info)
{
unsigned char (*sha1)[20] = info->shallow->sha1;
int i, dst;
- trace_printf_key(TRACE_KEY, "shallow: remove_nonexistent_theirs_shallow\n");
+ trace_printf_key(&trace_shallow, "shallow: remove_nonexistent_theirs_shallow\n");
for (i = dst = 0; i < info->nr_theirs; i++) {
if (i != dst)
info->theirs[dst] = info->theirs[i];
@@ -516,7 +516,7 @@ void assign_shallow_commits_to_refs(struct shallow_info *info,
int *shallow, nr_shallow = 0;
struct paint_info pi;
- trace_printf_key(TRACE_KEY, "shallow: assign_shallow_commits_to_refs\n");
+ trace_printf_key(&trace_shallow, "shallow: assign_shallow_commits_to_refs\n");
shallow = xmalloc(sizeof(*shallow) * (info->nr_ours + info->nr_theirs));
for (i = 0; i < info->nr_ours; i++)
shallow[nr_shallow++] = info->ours[i];
@@ -622,7 +622,7 @@ static void post_assign_shallow(struct shallow_info *info,
int bitmap_nr = (info->ref->nr + 31) / 32;
struct commit_array ca;
- trace_printf_key(TRACE_KEY, "shallow: post_assign_shallow\n");
+ trace_printf_key(&trace_shallow, "shallow: post_assign_shallow\n");
if (ref_status)
memset(ref_status, 0, sizeof(*ref_status) * info->ref->nr);
diff --git a/shell.c b/shell.c
index 5c0d47a5cc..ace62e4b65 100644
--- 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
index 0000000000..21485e2066
--- /dev/null
+++ b/split-index.c
@@ -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
index 0000000000..c1324f521a
--- /dev/null
+++ b/split-index.h
@@ -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
diff --git a/strbuf.c b/strbuf.c
index ac62982e67..33018d847f 100644
--- 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;
+}
diff --git a/strbuf.h b/strbuf.h
index e9ad03eabe..a7c0192e9e 100644
--- 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 */
diff --git a/string-list.c b/string-list.c
index aabb25ef4c..db38b62b46 100644
--- a/string-list.c
+++ b/string-list.c
@@ -1,6 +1,12 @@
#include "cache.h"
#include "string-list.h"
+void string_list_init(struct string_list *list, int strdup_strings)
+{
+ memset(list, 0, sizeof(*list));
+ list->strdup_strings = strdup_strings;
+}
+
/* if there is no exact match, point to the index where the entry could be
* inserted */
static int get_entry_index(const struct string_list *list, const char *string,
diff --git a/string-list.h b/string-list.h
index dd5e294465..494eb5d95d 100644
--- a/string-list.h
+++ b/string-list.h
@@ -18,6 +18,8 @@ struct string_list {
#define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0, NULL }
#define STRING_LIST_INIT_DUP { NULL, 0, 0, 1, NULL }
+void string_list_init(struct string_list *list, int strdup_strings);
+
void print_string_list(const struct string_list *p, const char *text);
void string_list_clear(struct string_list *list, int free_util);
diff --git a/submodule.c b/submodule.c
index b80ecacf60..c3a61e70f9 100644
--- a/submodule.c
+++ b/submodule.c
@@ -544,10 +544,7 @@ static int push_submodule(const char *path)
int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
{
int i, ret = 1;
- struct string_list needs_pushing;
-
- memset(&needs_pushing, 0, sizeof(struct string_list));
- needs_pushing.strdup_strings = 1;
+ struct string_list needs_pushing = STRING_LIST_INIT_DUP;
if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
return 1;
@@ -965,7 +962,7 @@ static int find_first_merges(struct object_array *result, const char *path,
sha1_to_hex(a->object.sha1));
init_revisions(&revs, NULL);
rev_opts.submodule = path;
- setup_revisions(sizeof(rev_args)/sizeof(char *)-1, rev_args, &revs, &rev_opts);
+ setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
/* save all revisions from the above list that contain b */
if (prepare_revision_walk(&revs))
diff --git a/symlinks.c b/symlinks.c
index c2b41a8501..5261e8cf49 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -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)
diff --git a/t/Makefile b/t/Makefile
index 8fd1a72357..43b15e36ae 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -13,7 +13,7 @@ TAR ?= $(TAR)
RM ?= rm -f
PROVE ?= prove
DEFAULT_TEST_TARGET ?= test
-TEST_LINT ?= test-lint-duplicates test-lint-executable
+TEST_LINT ?= test-lint
ifdef TEST_OUTPUT_DIRECTORY
TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
@@ -29,6 +29,7 @@ TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh))
TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
+THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
all: $(DEFAULT_TEST_TARGET)
@@ -65,7 +66,7 @@ test-lint-executable:
echo >&2 "non-executable tests:" $$bad; exit 1; }
test-lint-shell-syntax:
- @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T)
+ @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T) $(THELPERS)
aggregate-results-and-cleanup: $(T)
$(MAKE) aggregate-results
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 272fceef96..fd53b57187 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -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"
diff --git a/t/lib-httpd/error.sh b/t/lib-httpd/error.sh
index eafc9d2d90..a77b8e5469 100755
--- a/t/lib-httpd/error.sh
+++ b/t/lib-httpd/error.sh
@@ -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"
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
new file mode 100755
index 0000000000..79cdd34a54
--- /dev/null
+++ b/t/lib-submodule-update.sh
@@ -0,0 +1,680 @@
+# Create a submodule layout used for all tests below.
+#
+# The following use cases are covered:
+# - New submodule (no_submodule => add_sub1)
+# - Removed submodule (add_sub1 => remove_sub1)
+# - Updated submodule (add_sub1 => modify_sub1)
+# - Submodule updated to invalid commit (add_sub1 => invalid_sub1)
+# - Submodule updated from invalid commit (invalid_sub1 => valid_sub1)
+# - Submodule replaced by tracked files in directory (add_sub1 =>
+# replace_sub1_with_directory)
+# - Directory containing tracked files replaced by submodule
+# (replace_sub1_with_directory => replace_directory_with_sub1)
+# - Submodule replaced by tracked file with the same name (add_sub1 =>
+# replace_sub1_with_file)
+# - Tracked file replaced by submodule (replace_sub1_with_file =>
+# replace_file_with_sub1)
+#
+# --O-----O
+# / ^ replace_directory_with_sub1
+# / replace_sub1_with_directory
+# /----O
+# / ^
+# / modify_sub1
+# O------O-------O
+# ^ ^\ ^
+# | | \ remove_sub1
+# | | -----O-----O
+# | | \ ^ replace_file_with_sub1
+# | | \ replace_sub1_with_file
+# | add_sub1 --O-----O
+# no_submodule ^ valid_sub1
+# invalid_sub1
+#
+create_lib_submodule_repo () {
+ git init submodule_update_repo &&
+ (
+ cd submodule_update_repo &&
+ echo "expect" >>.gitignore &&
+ echo "actual" >>.gitignore &&
+ echo "x" >file1 &&
+ echo "y" >file2 &&
+ git add .gitignore file1 file2 &&
+ git commit -m "Base" &&
+ git branch "no_submodule" &&
+
+ git checkout -b "add_sub1" &&
+ git submodule add ./. sub1 &&
+ git config -f .gitmodules submodule.sub1.ignore all &&
+ git config submodule.sub1.ignore all &&
+ git add .gitmodules &&
+ git commit -m "Add sub1" &&
+ git checkout -b remove_sub1 &&
+ git revert HEAD &&
+
+ git checkout -b "modify_sub1" "add_sub1" &&
+ git submodule update &&
+ (
+ cd sub1 &&
+ git fetch &&
+ git checkout -b "modifications" &&
+ echo "z" >file2 &&
+ echo "x" >file3 &&
+ git add file2 file3 &&
+ git commit -m "modified file2 and added file3" &&
+ git push origin modifications
+ ) &&
+ git add sub1 &&
+ git commit -m "Modify sub1" &&
+
+ git checkout -b "replace_sub1_with_directory" "add_sub1" &&
+ git submodule update &&
+ (
+ cd sub1 &&
+ git checkout modifications
+ ) &&
+ git rm --cached sub1 &&
+ rm sub1/.git* &&
+ git config -f .gitmodules --remove-section "submodule.sub1" &&
+ git add .gitmodules sub1/* &&
+ git commit -m "Replace sub1 with directory" &&
+ git checkout -b replace_directory_with_sub1 &&
+ git revert HEAD &&
+
+ git checkout -b "replace_sub1_with_file" "add_sub1" &&
+ git rm sub1 &&
+ echo "content" >sub1 &&
+ git add sub1 &&
+ git commit -m "Replace sub1 with file" &&
+ git checkout -b replace_file_with_sub1 &&
+ git revert HEAD &&
+
+ git checkout -b "invalid_sub1" "add_sub1" &&
+ git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 sub1 &&
+ git commit -m "Invalid sub1 commit" &&
+ git checkout -b valid_sub1 &&
+ git revert HEAD &&
+ git checkout master
+ )
+}
+
+# Helper function to replace gitfile with .git directory
+replace_gitfile_with_git_dir () {
+ (
+ cd "$1" &&
+ git_dir="$(git rev-parse --git-dir)" &&
+ rm -f .git &&
+ cp -R "$git_dir" .git &&
+ GIT_WORK_TREE=. git config --unset core.worktree
+ )
+}
+
+# Test that the .git directory in the submodule is unchanged (except for the
+# core.worktree setting, which appears only in $GIT_DIR/modules/$1/config).
+# Call this function before test_submodule_content as the latter might
+# write the index file leading to false positive index differences.
+#
+# Note that this only supports submodules at the root level of the
+# superproject, with the default name, i.e. same as its path.
+test_git_directory_is_unchanged () {
+ (
+ cd ".git/modules/$1" &&
+ # does core.worktree point at the right place?
+ test "$(git config core.worktree)" = "../../../$1" &&
+ # remove it temporarily before comparing, as
+ # "$1/.git/config" lacks it...
+ git config --unset core.worktree
+ ) &&
+ diff -r ".git/modules/$1" "$1/.git" &&
+ (
+ # ... and then restore.
+ cd ".git/modules/$1" &&
+ git config core.worktree "../../../$1"
+ )
+}
+
+# Helper function to be executed at the start of every test below, it sets up
+# the submodule repo if it doesn't exist and configures the most problematic
+# settings for diff.ignoreSubmodules.
+prolog () {
+ (test -d submodule_update_repo || create_lib_submodule_repo) &&
+ test_config_global diff.ignoreSubmodules all &&
+ test_config diff.ignoreSubmodules all
+}
+
+# Helper function to bring work tree back into the state given by the
+# commit. This includes trying to populate sub1 accordingly if it exists and
+# should be updated to an existing commit.
+reset_work_tree_to () {
+ rm -rf submodule_update &&
+ git clone submodule_update_repo submodule_update &&
+ (
+ cd submodule_update &&
+ rm -rf sub1 &&
+ git checkout -f "$1" &&
+ git status -u -s >actual &&
+ test_must_be_empty actual &&
+ sha1=$(git rev-parse --revs-only HEAD:sub1) &&
+ if test -n "$sha1" &&
+ test $(cd "sub1" && git rev-parse --verify "$sha1^{commit}")
+ then
+ git submodule update --init --recursive "sub1"
+ fi
+ )
+}
+
+# Test that the superproject contains the content according to commit "$1"
+# (the work tree must match the index for everything but submodules but the
+# index must exactly match the given commit including any submodule SHA-1s).
+test_superproject_content () {
+ git diff-index --cached "$1" >actual &&
+ test_must_be_empty actual &&
+ git diff-files --ignore-submodules >actual &&
+ test_must_be_empty actual
+}
+
+# Test that the given submodule at path "$1" contains the content according
+# to the submodule commit recorded in the superproject's commit "$2"
+test_submodule_content () {
+ if test $# != 2
+ then
+ echo "test_submodule_content needs two arguments"
+ return 1
+ fi &&
+ submodule="$1" &&
+ commit="$2" &&
+ test -d "$submodule"/ &&
+ if ! test -f "$submodule"/.git && ! test -d "$submodule"/.git
+ then
+ echo "Submodule $submodule is not populated"
+ return 1
+ fi &&
+ sha1=$(git rev-parse --verify "$commit:$submodule") &&
+ if test -z "$sha1"
+ then
+ echo "Couldn't retrieve SHA-1 of $submodule for $commit"
+ return 1
+ fi &&
+ (
+ cd "$submodule" &&
+ git status -u -s >actual &&
+ test_must_be_empty actual &&
+ git diff "$sha1" >actual &&
+ test_must_be_empty actual
+ )
+}
+
+# Test that the following transitions are correctly handled:
+# - Updated submodule
+# - New submodule
+# - Removed submodule
+# - Directory containing tracked files replaced by submodule
+# - Submodule replaced by tracked files in directory
+# - Submodule replaced by tracked file with the same name
+# - tracked file replaced by submodule
+#
+# The default is that submodule contents aren't changed until "git submodule
+# update" is run. And even then that command doesn't delete the work tree of
+# a removed submodule.
+#
+# Removing a submodule containing a .git directory must fail even when forced
+# to protect the history!
+#
+
+# Test that submodule contents are currently not updated when switching
+# between commits that change a submodule.
+test_submodule_switch () {
+ command="$1"
+ ######################### Appearing submodule #########################
+ # Switching to a commit letting a submodule appear creates empty dir ...
+ if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1
+ then
+ # Restoring stash fails to restore submodule index entry
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: added submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... and doesn't care if it already exists ...
+ test_expect_$RESULT "$command: added submodule leaves existing empty directory alone" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ mkdir sub1 &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... unless there is an untracked file in its place.
+ test_expect_success "$command: added submodule doesn't remove untracked unignored file with same name" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ >sub1 &&
+ test_must_fail $command add_sub1 &&
+ test_superproject_content origin/no_submodule &&
+ test_must_be_empty sub1
+ )
+ '
+ # Replacing a tracked file with a submodule produces an empty
+ # directory ...
+ test_expect_$RESULT "$command: replace tracked file with submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_file &&
+ (
+ cd submodule_update &&
+ git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
+ $command replace_file_with_sub1 &&
+ test_superproject_content origin/replace_file_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_file_with_sub1
+ )
+ '
+ # ... as does removing a directory with tracked files with a
+ # submodule.
+ if test "$KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR" = 1
+ then
+ # Non fast-forward merges fail with "Directory sub1 doesn't
+ # exist. sub1" because the empty submodule directory is not
+ # created
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: replace directory with submodule" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_directory &&
+ (
+ cd submodule_update &&
+ git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
+ $command replace_directory_with_sub1 &&
+ test_superproject_content origin/replace_directory_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_directory_with_sub1
+ )
+ '
+
+ ######################## Disappearing submodule #######################
+ # Removing a submodule doesn't remove its work tree ...
+ if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1
+ then
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: removed submodule leaves submodule directory and its contents in place" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_$RESULT "$command: removed submodule leaves submodule containing a .git directory alone" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ replace_gitfile_with_git_dir sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing a submodule with files in a directory must fail as the
+ # submodule work tree isn't removed ...
+ if test "$KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES" = 1
+ then
+ # Non fast-forward merges attempt to merge the former
+ # submodule files with the newly checked out ones in the
+ # directory of the same name while it shouldn't.
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: replace submodule with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_$RESULT "$command: replace submodule containing a .git directory with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing it with a file must fail as it could throw away any local
+ # work tree changes ...
+ test_expect_failure "$command: replace submodule with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... or even destroy unpushed parts of submodule history if that
+ # still uses a .git directory.
+ test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+
+ ########################## Modified submodule #########################
+ # Updating a submodule sha1 doesn't update the submodule's work tree
+ if test "$KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT" = 1
+ then
+ # When cherry picking a SHA-1 update for an ignored submodule
+ # the commit incorrectly fails with "The previous cherry-pick
+ # is now empty, possibly due to conflict resolution."
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: modified submodule does not update submodule work tree" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t modify_sub1 origin/modify_sub1 &&
+ $command modify_sub1 &&
+ test_superproject_content origin/modify_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ git submodule update &&
+ test_submodule_content sub1 origin/modify_sub1
+ )
+ '
+
+ # Updating a submodule to an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will fail
+ test_expect_$RESULT "$command: modified submodule does not update submodule work tree to invalid commit" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t invalid_sub1 origin/invalid_sub1 &&
+ $command invalid_sub1 &&
+ test_superproject_content origin/invalid_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ test_must_fail git submodule update &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Updating a submodule from an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will succeed
+ test_expect_$RESULT "$command: modified submodule does not update submodule work tree from invalid commit" '
+ prolog &&
+ reset_work_tree_to invalid_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t valid_sub1 origin/valid_sub1 &&
+ $command valid_sub1 &&
+ test_superproject_content origin/valid_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/valid_sub1
+ )
+ '
+}
+
+# Test that submodule contents are currently not updated when switching
+# between commits that change a submodule, but throwing away local changes in
+# the superproject is allowed.
+test_submodule_forced_switch () {
+ command="$1"
+ ######################### Appearing submodule #########################
+ # Switching to a commit letting a submodule appear creates empty dir ...
+ test_expect_success "$command: added submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... and doesn't care if it already exists ...
+ test_expect_success "$command: added submodule leaves existing empty directory alone" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ mkdir sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... unless there is an untracked file in its place.
+ test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ >sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1
+ )
+ '
+ # Replacing a tracked file with a submodule produces an empty
+ # directory ...
+ test_expect_success "$command: replace tracked file with submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_file &&
+ (
+ cd submodule_update &&
+ git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
+ $command replace_file_with_sub1 &&
+ test_superproject_content origin/replace_file_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_file_with_sub1
+ )
+ '
+ # ... as does removing a directory with tracked files with a
+ # submodule.
+ test_expect_success "$command: replace directory with submodule" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_directory &&
+ (
+ cd submodule_update &&
+ git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
+ $command replace_directory_with_sub1 &&
+ test_superproject_content origin/replace_directory_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_directory_with_sub1
+ )
+ '
+
+ ######################## Disappearing submodule #######################
+ # Removing a submodule doesn't remove its work tree ...
+ test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ replace_gitfile_with_git_dir sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing a submodule with files in a directory must fail as the
+ # submodule work tree isn't removed ...
+ test_expect_failure "$command: replace submodule with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_failure "$command: replace submodule containing a .git directory with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing it with a file must fail as it could throw away any local
+ # work tree changes ...
+ test_expect_failure "$command: replace submodule with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... or even destroy unpushed parts of submodule history if that
+ # still uses a .git directory.
+ test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+
+ ########################## Modified submodule #########################
+ # Updating a submodule sha1 doesn't update the submodule's work tree
+ test_expect_success "$command: modified submodule does not update submodule work tree" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t modify_sub1 origin/modify_sub1 &&
+ $command modify_sub1 &&
+ test_superproject_content origin/modify_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ git submodule update &&
+ test_submodule_content sub1 origin/modify_sub1
+ )
+ '
+ # Updating a submodule to an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will fail
+ test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t invalid_sub1 origin/invalid_sub1 &&
+ $command invalid_sub1 &&
+ test_superproject_content origin/invalid_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ test_must_fail git submodule update &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Updating a submodule from an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will succeed
+ test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" '
+ prolog &&
+ reset_work_tree_to invalid_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t valid_sub1 origin/valid_sub1 &&
+ $command valid_sub1 &&
+ test_superproject_content origin/valid_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/valid_sub1
+ )
+ '
+}
diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh
index 685d46f8b7..f8ed8573b7 100755
--- a/t/perf/p5310-pack-bitmaps.sh
+++ b/t/perf/p5310-pack-bitmaps.sh
@@ -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
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 2f3020342a..e62c0ffbc2 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -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 &&
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index 5ef5ad3db4..39e55a13c8 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -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 &&
diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh
index 391e2b6492..f97c80556f 100755
--- a/t/t0011-hashmap.sh
+++ b/t/t0011-hashmap.sh
@@ -237,4 +237,17 @@ test_expect_success 'grow / shrink' '
'
+test_expect_success 'string interning' '
+
+test_hashmap "intern value1
+intern Value1
+intern value2
+intern value2
+" "value1
+Value1
+value2
+value2"
+
+'
+
test_done
diff --git a/t/t0025-crlf-auto.sh b/t/t0025-crlf-auto.sh
index b0e5694ebd..c164b4662a 100755
--- a/t/t0025-crlf-auto.sh
+++ b/t/t0025-crlf-auto.sh
@@ -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
diff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh
index e1126aa7cc..4807b0f015 100755
--- a/t/t0026-eol-config.sh
+++ b/t/t0026-eol-config.sh
@@ -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
index 0000000000..72dd3e8bb4
--- /dev/null
+++ b/t/t0027-auto-crlf.sh
@@ -0,0 +1,265 @@
+#!/bin/sh
+
+test_description='CRLF conversion all combinations'
+
+. ./test-lib.sh
+
+if ! test_have_prereq EXPENSIVE
+then
+ skip_all="EXPENSIVE not set"
+ test_done
+fi
+
+
+compare_files()
+{
+ od -c <"$1" >"$1".expect &&
+ od -c <"$2" >"$2".actual &&
+ test_cmp "$1".expect "$2".actual &&
+ rm "$1".expect "$2".actual
+}
+
+compare_ws_file()
+{
+ pfx=$1
+ exp=$2.expect
+ act=$pfx.actual.$3
+ od -c <"$2" >"$exp" &&
+ od -c <"$3" >"$act" &&
+ test_cmp $exp $act &&
+ rm $exp $act
+}
+
+create_gitattributes()
+{
+ txtbin=$1
+ case "$txtbin" in
+ auto)
+ echo "*.txt text=auto" >.gitattributes
+ ;;
+ text)
+ echo "*.txt text" >.gitattributes
+ ;;
+ -text)
+ echo "*.txt -text" >.gitattributes
+ ;;
+ *)
+ echo >.gitattributes
+ ;;
+ esac
+}
+
+create_file_in_repo()
+{
+ crlf=$1
+ txtbin=$2
+ create_gitattributes "$txtbin" &&
+ for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
+ do
+ pfx=crlf_${crlf}_attr_${txtbin}_$f.txt &&
+ cp $f $pfx && git -c core.autocrlf=$crlf add $pfx
+ done &&
+ git commit -m "core.autocrlf $crlf"
+}
+
+check_files_in_repo()
+{
+ crlf=$1
+ txtbin=$2
+ lfname=$3
+ crlfname=$4
+ lfmixcrlf=$5
+ lfmixcr=$6
+ crlfnul=$7
+ pfx=crlf_${crlf}_attr_${txtbin}_ &&
+ compare_files $lfname ${pfx}LF.txt &&
+ compare_files $crlfname ${pfx}CRLF.txt &&
+ compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt &&
+ compare_files $lfmixcr ${pfx}LF_mix_CR.txt &&
+ compare_files $crlfnul ${pfx}CRLF_nul.txt
+}
+
+
+check_files_in_ws()
+{
+ eol=$1
+ crlf=$2
+ txtbin=$3
+ lfname=$4
+ crlfname=$5
+ lfmixcrlf=$6
+ lfmixcr=$7
+ crlfnul=$8
+ create_gitattributes $txtbin &&
+ git config core.autocrlf $crlf &&
+ pfx=eol_${eol}_crlf_${crlf}_attr_${txtbin}_ &&
+ src=crlf_false_attr__ &&
+ for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
+ do
+ rm $src$f.txt &&
+ if test -z "$eol"; then
+ git checkout $src$f.txt
+ else
+ git -c core.eol=$eol checkout $src$f.txt
+ fi
+ done
+
+
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=LF" "
+ compare_ws_file $pfx $lfname ${src}LF.txt
+ "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF" "
+ compare_ws_file $pfx $crlfname ${src}CRLF.txt
+ "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF_mix_LF" "
+ compare_ws_file $pfx $lfmixcrlf ${src}CRLF_mix_LF.txt
+ "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=LF_mix_CR" "
+ compare_ws_file $pfx $lfmixcr ${src}LF_mix_CR.txt
+ "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF_nul" "
+ compare_ws_file $pfx $crlfnul ${src}CRLF_nul.txt
+ "
+}
+
+#######
+(
+ type od >/dev/null &&
+ printf "line1Q\r\nline2\r\nline3" | q_to_nul >CRLF_nul &&
+ cat >expect <<-EOF &&
+ 0000000 l i n e 1 \0 \r \n l i n e 2 \r \n l
+ 0000020 i n e 3
+ 0000024
+EOF
+ od -c CRLF_nul | sed -e "s/[ ][ ]*/ /g" -e "s/ *$//" >actual
+ test_cmp expect actual &&
+ rm expect actual
+) || {
+ skip_all="od not found or od -c not usable"
+ exit 0
+ test_done
+}
+
+test_expect_success 'setup master' '
+ echo >.gitattributes &&
+ git checkout -b master &&
+ git add .gitattributes &&
+ git commit -m "add .gitattributes" "" &&
+ printf "line1\nline2\nline3" >LF &&
+ printf "line1\r\nline2\r\nline3" >CRLF &&
+ printf "line1\r\nline2\nline3" >CRLF_mix_LF &&
+ printf "line1\nline2\rline3" >LF_mix_CR &&
+ printf "line1\r\nline2\rline3" >CRLF_mix_CR &&
+ printf "line1Q\nline2\nline3" | q_to_nul >LF_nul
+'
+# CRLF_nul had been created above
+
+test_expect_success 'create files' '
+ create_file_in_repo false "" &&
+ create_file_in_repo true "" &&
+ create_file_in_repo input "" &&
+
+ create_file_in_repo false "auto" &&
+ create_file_in_repo true "auto" &&
+ create_file_in_repo input "auto" &&
+
+ create_file_in_repo false "text" &&
+ create_file_in_repo true "text" &&
+ create_file_in_repo input "text" &&
+
+ create_file_in_repo false "-text" &&
+ create_file_in_repo true "-text" &&
+ create_file_in_repo input "-text" &&
+ rm -f *.txt &&
+ git reset --hard
+'
+
+test_expect_success 'commit empty gitattribues' '
+ check_files_in_repo false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo true "" LF LF LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo input "" LF LF LF LF_mix_CR CRLF_nul
+'
+
+test_expect_success 'commit text=auto' '
+ check_files_in_repo false "auto" LF LF LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo true "auto" LF LF LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo input "auto" LF LF LF LF_mix_CR CRLF_nul
+'
+
+test_expect_success 'commit text' '
+ check_files_in_repo false "text" LF LF LF LF_mix_CR LF_nul &&
+ check_files_in_repo true "text" LF LF LF LF_mix_CR LF_nul &&
+ check_files_in_repo input "text" LF LF LF LF_mix_CR LF_nul
+'
+
+test_expect_success 'commit -text' '
+ check_files_in_repo false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+'
+
+################################################################################
+# Check how files in the repo are changed when they are checked out
+# How to read the table below:
+# - check_files_in_ws will check multiple files, see below
+# - parameter $1 : core.eol lf | crlf
+# - parameter $2 : core.autocrlf false | true | input
+# - parameter $3 : text in .gitattributs "" (empty) | auto | text | -text
+# - parameter $4 : reference for a file with only LF in the repo
+# - parameter $5 : reference for a file with only CRLF in the repo
+# - parameter $6 : reference for a file with mixed LF and CRLF in the repo
+# - parameter $7 : reference for a file with LF and CR in the repo (does somebody uses this ?)
+# - parameter $8 : reference for a file with CRLF and a NUL (should be handled as binary when auto)
+
+check_files_in_ws lf false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf input "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_files_in_ws lf false "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws lf input "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_files_in_ws lf false "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws lf input "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_files_in_ws lf false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+###########
+#core.autocrlf=input is forbidden with core.eol=crlf
+check_files_in_ws crlf false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws crlf true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_files_in_ws crlf false "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws crlf true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+
+check_files_in_ws crlf false "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws crlf true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+
+check_files_in_ws crlf false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws crlf true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+if test_have_prereq MINGW
+then
+check_files_in_ws "" false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" false "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws "" false "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws "" false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_files_in_ws native false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native false "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws native false "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws native false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+fi
+
+test_done
diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh
new file mode 100755
index 0000000000..20526aed34
--- /dev/null
+++ b/t/t1013-read-tree-submodule.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='read-tree can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+test_submodule_switch "git read-tree -u -m"
+
+test_submodule_forced_switch "git read-tree -u --reset"
+
+test_done
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
index 1a5a5f39fd..9aeb352b3d 100755
--- a/t/t1402-check-ref-format.sh
+++ b/t/t1402-check-ref-format.sh
@@ -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
index 0000000000..94fb473e7c
--- /dev/null
+++ b/t/t1700-split-index.sh
@@ -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
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index 06b18f8bc1..6847f75822 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -3,6 +3,7 @@
test_description='checkout can handle submodules'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
test_expect_success 'setup' '
mkdir submodule &&
@@ -62,4 +63,8 @@ test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/
! test -s actual
'
+test_submodule_switch "git checkout"
+
+test_submodule_forced_switch "git checkout -f"
+
test_done
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
index 29c1fb10ca..cc830da58d 100755
--- a/t/t2104-update-index-skip-worktree.sh
+++ b/t/t2104-update-index-skip-worktree.sh
@@ -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
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 80e0a951ea..47b5682662 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -169,6 +169,29 @@ test_expect_success 'default to common base in @{upstream}s reflog if no upstrea
test_cmp expect actual
'
+test_expect_success 'cherry-picked commits and fork-point work together' '
+ git checkout default-base &&
+ echo Amended >A &&
+ git commit -a --no-edit --amend &&
+ test_commit B B &&
+ test_commit new_B B "New B" &&
+ test_commit C C &&
+ git checkout default &&
+ git reset --hard default-base@{4} &&
+ test_commit D D &&
+ git cherry-pick -2 default-base^ &&
+ test_commit final_B B "Final B" &&
+ git rebase &&
+ echo Amended >expect &&
+ test_cmp A expect &&
+ echo "Final B" >expect &&
+ test_cmp B expect &&
+ echo C >expect &&
+ test_cmp C expect &&
+ echo D >expect &&
+ test_cmp D expect
+'
+
test_expect_success 'rebase -q is quiet' '
git checkout -b quiet topic &&
git rebase -q master >output.out 2>&1 &&
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index be8c1d5ef9..5a27ec9b5e 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -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
diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh
new file mode 100755
index 0000000000..d5b896d445
--- /dev/null
+++ b/t/t3426-rebase-submodule.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='rebase can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+git_rebase () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ git checkout -b ours HEAD &&
+ echo x >>file1 &&
+ git add file1 &&
+ git commit -m add_x &&
+ git revert HEAD &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git rebase "$1"
+}
+
+test_submodule_switch "git_rebase"
+
+git_rebase_interactive () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ git checkout -b ours HEAD &&
+ echo x >>file1 &&
+ git add file1 &&
+ git commit -m add_x &&
+ git revert HEAD &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ set_fake_editor &&
+ echo "fake-editor.sh" >.git/info/exclude &&
+ git rebase -i "$1"
+}
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+# The real reason "replace directory with submodule" fails is because a
+# directory "sub1" exists, but we reuse the suppression added for merge here
+test_submodule_switch "git_rebase_interactive"
+
+test_done
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
new file mode 100755
index 0000000000..6863b7bb6f
--- /dev/null
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+test_description='cherry-pick can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git cherry-pick"
+
+test_done
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
new file mode 100755
index 0000000000..a1c4e0216f
--- /dev/null
+++ b/t/t3513-revert-submodule.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='revert can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+# Create a revert that moves from HEAD (including any test modifications to
+# the work tree) to $1 by first checking out $1 and reverting it. Reverting
+# the revert is the transition we test for. We tar the current work tree
+# first so we can restore the work tree test setup after doing the checkout
+# and revert. We test here that the restored work tree content is identical
+# to that at the beginning. The last revert is then tested by the framework.
+git_revert () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ tar czf "$TRASH_DIRECTORY/tmp.tgz" * &&
+ git checkout "$1" &&
+ git revert HEAD &&
+ rm -rf * &&
+ tar xzf "$TRASH_DIRECTORY/tmp.tgz" &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git revert HEAD
+}
+
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+test_submodule_switch "git_revert"
+
+test_done
diff --git a/t/t3906-stash-submodule.sh b/t/t3906-stash-submodule.sh
new file mode 100755
index 0000000000..d7219d6f8f
--- /dev/null
+++ b/t/t3906-stash-submodule.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='stash apply can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+git_stash () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ git read-tree -u -m "$1" &&
+ git stash &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git stash apply
+}
+
+KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES=1
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+test_submodule_switch "git_stash"
+
+test_done
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
index 49e2d6c349..fae305979a 100755
--- a/t/t4102-apply-rename.sh
+++ b/t/t4102-apply-rename.sh
@@ -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
diff --git a/t/t4137-apply-submodule.sh b/t/t4137-apply-submodule.sh
new file mode 100755
index 0000000000..a9bd40a6d0
--- /dev/null
+++ b/t/t4137-apply-submodule.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='git apply handling submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+apply_index () {
+ git diff --ignore-submodules=dirty "..$1" | git apply --index -
+}
+
+test_submodule_switch "apply_index"
+
+apply_3way () {
+ git diff --ignore-submodules=dirty "..$1" | git apply --3way -
+}
+
+test_submodule_switch "apply_3way"
+
+test_done
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index cb03d28769..99ab7ca21f 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -3,6 +3,7 @@
test_description='git log'
. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
test_expect_success setup '
@@ -841,4 +842,34 @@ test_expect_success 'dotdot is a parent directory' '
test_cmp expect actual
'
+test_expect_success GPG 'log --graph --show-signature' '
+ test_when_finished "git reset --hard && git checkout master" &&
+ git checkout -b signed master &&
+ echo foo >foo &&
+ git add foo &&
+ git commit -S -m signed_commit &&
+ git log --graph --show-signature -n1 signed >actual &&
+ grep "^| gpg: Signature made" actual &&
+ grep "^| gpg: Good signature" actual
+'
+
+test_expect_success GPG 'log --graph --show-signature for merged tag' '
+ test_when_finished "git reset --hard && git checkout master" &&
+ git checkout -b plain master &&
+ echo aaa >bar &&
+ git add bar &&
+ git commit -m bar_commit &&
+ git checkout -b tagged master &&
+ echo bbb >baz &&
+ git add baz &&
+ git commit -m baz_commit &&
+ git tag -s -m signed_tag_msg signed_tag &&
+ git checkout plain &&
+ git merge --no-ff -m msg signed_tag &&
+ git log --graph --show-signature -n1 plain >actual &&
+ grep "^|\\\ merged tag" actual &&
+ grep "^| | gpg: Signature made" actual &&
+ grep "^| | gpg: Good signature" actual
+'
+
test_done
diff --git a/t/t4255-am-submodule.sh b/t/t4255-am-submodule.sh
new file mode 100755
index 0000000000..8bde7dbb6d
--- /dev/null
+++ b/t/t4255-am-submodule.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+test_description='git am handling submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+am () {
+ git format-patch --stdout --ignore-submodules=dirty "..$1" | git am -
+}
+
+test_submodule_switch "am"
+
+am_3way () {
+ git format-patch --stdout --ignore-submodules=dirty "..$1" | git am --3way -
+}
+
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "am_3way"
+
+test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index bad84a36e8..7b8babd89b 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -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 &&
diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh
index 21a5c93f41..c929db5633 100755
--- a/t/t5003-archive-zip.sh
+++ b/t/t5003-archive-zip.sh
@@ -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 &&
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index f4f02ba918..0580258c91 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -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
'
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
index 1753ef2b91..fc898c9eac 100755
--- a/t/t5403-post-checkout-hook.sh
+++ b/t/t5403-post-checkout-hook.sh
@@ -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
diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh
index 94553e1039..b46118846c 100755
--- a/t/t5539-fetch-http-shallow.sh
+++ b/t/t5539-fetch-http-shallow.sh
@@ -54,6 +54,7 @@ EOF
test_expect_success 'no shallow lines after receiving ACK ready' '
(
cd shallow &&
+ test_tick &&
for i in $(test_seq 15)
do
git checkout --orphan unrelated$i &&
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 01b8aae2ed..ac71418a1b 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -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
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
new file mode 100755
index 0000000000..accfa5cc0c
--- /dev/null
+++ b/t/t5572-pull-submodule.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='pull can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+reset_branch_to_HEAD () {
+ git branch -D "$1" &&
+ git checkout -b "$1" HEAD &&
+ git branch --set-upstream-to="origin/$1" "$1"
+}
+
+git_pull () {
+ reset_branch_to_HEAD "$1" &&
+ git pull
+}
+
+# pulls without conflicts
+test_submodule_switch "git_pull"
+
+git_pull_ff () {
+ reset_branch_to_HEAD "$1" &&
+ git pull --ff
+}
+
+test_submodule_switch "git_pull_ff"
+
+git_pull_ff_only () {
+ reset_branch_to_HEAD "$1" &&
+ git pull --ff-only
+}
+
+test_submodule_switch "git_pull_ff_only"
+
+git_pull_noff () {
+ reset_branch_to_HEAD "$1" &&
+ git pull --no-ff
+}
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git_pull_noff"
+
+test_done
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index c277db64f7..88ed3191e8 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -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
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index 432f086c06..3758961765 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -77,12 +77,29 @@ test_expect_success "merge without conflict (--quiet)" \
"git merge-file --quiet test.txt orig.txt new2.txt"
cp new1.txt test2.txt
-test_expect_success "merge without conflict (missing LF at EOF)" \
- "git merge-file test2.txt orig.txt new2.txt"
+test_expect_failure "merge without conflict (missing LF at EOF)" \
+ "git merge-file test2.txt orig.txt new4.txt"
-test_expect_success "merge result added missing LF" \
+test_expect_failure "merge result added missing LF" \
"test_cmp test.txt test2.txt"
+cp new4.txt test3.txt
+test_expect_success "merge without conflict (missing LF at EOF, away from change in the other file)" \
+ "git merge-file --quiet test3.txt new2.txt new3.txt"
+
+cat > expect.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+EOF
+printf "propter nomen suum." >> expect.txt
+
+test_expect_success "merge does not add LF away of change" \
+ "test_cmp test3.txt expect.txt"
+
cp test.txt backup.txt
test_expect_success "merge with conflicts" \
"test_must_fail git merge-file test.txt orig.txt new3.txt"
@@ -107,6 +124,55 @@ EOF
test_expect_success "expected conflict markers" "test_cmp test.txt expect.txt"
cp backup.txt test.txt
+
+cat > expect.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --ours" \
+ "git merge-file --ours test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
+cat > expect.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --theirs" \
+ "git merge-file --theirs test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
+cat > expect.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --union" \
+ "git merge-file --union test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
test_expect_success "merge with conflicts, using -L" \
"test_must_fail git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
@@ -260,4 +326,23 @@ test_expect_success 'marker size' '
test_cmp expect actual
'
+printf "line1\nline2\nline3" >nolf-orig.txt
+printf "line1\nline2\nline3x" >nolf-diff1.txt
+printf "line1\nline2\nline3y" >nolf-diff2.txt
+
+test_expect_success 'conflict at EOF without LF resolved by --ours' \
+ 'git merge-file -p --ours nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+ printf "line1\nline2\nline3x" >expect.txt &&
+ test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict at EOF without LF resolved by --theirs' \
+ 'git merge-file -p --theirs nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+ printf "line1\nline2\nline3y" >expect.txt &&
+ test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict at EOF without LF resolved by --union' \
+ 'git merge-file -p --union nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+ printf "line1\nline2\nline3x\nline3y" >expect.txt &&
+ test_cmp expect.txt output.txt'
+
test_done
diff --git a/t/t6041-bisect-submodule.sh b/t/t6041-bisect-submodule.sh
new file mode 100755
index 0000000000..c6b7aa6977
--- /dev/null
+++ b/t/t6041-bisect-submodule.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='bisect can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+git_bisect () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ tar czf "$TRASH_DIRECTORY/tmp.tgz" * &&
+ GOOD=$(git rev-parse --verify HEAD) &&
+ git checkout "$1" &&
+ echo "foo" >bar &&
+ git add bar &&
+ git commit -m "bisect bad" &&
+ BAD=$(git rev-parse --verify HEAD) &&
+ git reset --hard HEAD^^ &&
+ git submodule update &&
+ git bisect start &&
+ git bisect good $GOOD &&
+ rm -rf * &&
+ tar xzf "$TRASH_DIRECTORY/tmp.tgz" &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git bisect bad $BAD
+}
+
+test_submodule_switch "git_bisect"
+
+test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index 68b3cb26d9..4d5a25eedf 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -7,8 +7,9 @@ test_description='Tests replace refs functionality'
exec </dev/null
. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
-add_and_commit_file()
+add_and_commit_file ()
{
_file="$1"
_msg="$2"
@@ -18,6 +19,38 @@ add_and_commit_file()
git commit --quiet -m "$_file: $_msg"
}
+commit_buffer_contains_parents ()
+{
+ git cat-file commit "$1" >payload &&
+ sed -n -e '/^$/q' -e '/^parent /p' <payload >actual &&
+ shift &&
+ for _parent
+ do
+ echo "parent $_parent"
+ done >expected &&
+ test_cmp expected actual
+}
+
+commit_peeling_shows_parents ()
+{
+ _parent_number=1
+ _commit="$1"
+ shift &&
+ for _parent
+ do
+ _found=$(git rev-parse --verify $_commit^$_parent_number) || return 1
+ test "$_found" = "$_parent" || return 1
+ _parent_number=$(( $_parent_number + 1 ))
+ done &&
+ test_must_fail git rev-parse --verify $_commit^$_parent_number
+}
+
+commit_has_parents ()
+{
+ commit_buffer_contains_parents "$@" &&
+ commit_peeling_shows_parents "$@"
+}
+
HASH1=
HASH2=
HASH3=
@@ -27,36 +60,36 @@ HASH6=
HASH7=
test_expect_success 'set up buggy branch' '
- echo "line 1" >> hello &&
- echo "line 2" >> hello &&
- echo "line 3" >> hello &&
- echo "line 4" >> hello &&
+ echo "line 1" >>hello &&
+ echo "line 2" >>hello &&
+ echo "line 3" >>hello &&
+ echo "line 4" >>hello &&
add_and_commit_file hello "4 lines" &&
HASH1=$(git rev-parse --verify HEAD) &&
- echo "line BUG" >> hello &&
- echo "line 6" >> hello &&
- echo "line 7" >> hello &&
- echo "line 8" >> hello &&
+ echo "line BUG" >>hello &&
+ echo "line 6" >>hello &&
+ echo "line 7" >>hello &&
+ echo "line 8" >>hello &&
add_and_commit_file hello "4 more lines with a BUG" &&
HASH2=$(git rev-parse --verify HEAD) &&
- echo "line 9" >> hello &&
- echo "line 10" >> hello &&
+ echo "line 9" >>hello &&
+ echo "line 10" >>hello &&
add_and_commit_file hello "2 more lines" &&
HASH3=$(git rev-parse --verify HEAD) &&
- echo "line 11" >> hello &&
+ echo "line 11" >>hello &&
add_and_commit_file hello "1 more line" &&
HASH4=$(git rev-parse --verify HEAD) &&
- sed -e "s/BUG/5/" hello > hello.new &&
+ sed -e "s/BUG/5/" hello >hello.new &&
mv hello.new hello &&
add_and_commit_file hello "BUG fixed" &&
HASH5=$(git rev-parse --verify HEAD) &&
- echo "line 12" >> hello &&
- echo "line 13" >> hello &&
+ echo "line 12" >>hello &&
+ echo "line 13" >>hello &&
add_and_commit_file hello "2 more lines" &&
HASH6=$(git rev-parse --verify HEAD) &&
- echo "line 14" >> hello &&
- echo "line 15" >> hello &&
- echo "line 16" >> hello &&
+ echo "line 14" >>hello &&
+ echo "line 15" >>hello &&
+ echo "line 16" >>hello &&
add_and_commit_file hello "again 3 more lines" &&
HASH7=$(git rev-parse --verify HEAD)
'
@@ -95,7 +128,7 @@ test_expect_success 'tag replaced commit' '
'
test_expect_success '"git fsck" works' '
- git fsck master > fsck_master.out &&
+ git fsck master >fsck_master.out &&
grep "dangling commit $R" fsck_master.out &&
grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
test -z "$(git fsck)"
@@ -217,14 +250,14 @@ test_expect_success 'fetch branch with replacement' '
(
cd clone_dir &&
git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
- git log --pretty=oneline parallel3 > output.txt &&
+ git log --pretty=oneline parallel3 >output.txt &&
! grep $PARA3 output.txt &&
- git show $PARA3 > para3.txt &&
+ git show $PARA3 >para3.txt &&
grep "A U Thor" para3.txt &&
git fetch origin "refs/replace/*:refs/replace/*" &&
- git log --pretty=oneline parallel3 > output.txt &&
+ git log --pretty=oneline parallel3 >output.txt &&
grep $PARA3 output.txt &&
- git show $PARA3 > para3.txt &&
+ git show $PARA3 >para3.txt &&
grep "O Thor" para3.txt
)
'
@@ -302,7 +335,7 @@ test_expect_success 'test --format medium' '
echo "$PARA3 -> $S" &&
echo "$MYTAG -> $HASH1"
} | sort >expected &&
- git replace -l --format medium | sort > actual &&
+ git replace -l --format medium | sort >actual &&
test_cmp expected actual
'
@@ -314,7 +347,7 @@ test_expect_success 'test --format long' '
echo "$PARA3 (commit) -> $S (commit)" &&
echo "$MYTAG (tag) -> $HASH1 (commit)"
} | sort >expected &&
- git replace --format=long | sort > actual &&
+ git replace --format=long | sort >actual &&
test_cmp expected actual
'
@@ -351,4 +384,60 @@ test_expect_success 'replace ref cleanup' '
test -z "$(git replace)"
'
+test_expect_success '--graft with and without already replaced object' '
+ test $(git log --oneline | wc -l) = 7 &&
+ git replace --graft $HASH5 &&
+ test $(git log --oneline | wc -l) = 3 &&
+ commit_has_parents $HASH5 &&
+ test_must_fail git replace --graft $HASH5 $HASH4 $HASH3 &&
+ git replace --force -g $HASH5 $HASH4 $HASH3 &&
+ commit_has_parents $HASH5 $HASH4 $HASH3 &&
+ git replace -d $HASH5
+'
+
+test_expect_success GPG 'set up a signed commit' '
+ echo "line 17" >>hello &&
+ echo "line 18" >>hello &&
+ git add hello &&
+ test_tick &&
+ git commit --quiet -S -m "hello: 2 more lines in a signed commit" &&
+ HASH8=$(git rev-parse --verify HEAD) &&
+ git verify-commit $HASH8
+'
+
+test_expect_success GPG '--graft with a signed commit' '
+ git cat-file commit $HASH8 >orig &&
+ git replace --graft $HASH8 &&
+ git cat-file commit $HASH8 >repl &&
+ commit_has_parents $HASH8 &&
+ test_must_fail git verify-commit $HASH8 &&
+ sed -n -e "/^tree /p" -e "/^author /p" -e "/^committer /p" orig >expected &&
+ echo >>expected &&
+ sed -e "/^$/q" repl >actual &&
+ test_cmp expected actual &&
+ git replace -d $HASH8
+'
+
+test_expect_success GPG 'set up a merge commit with a mergetag' '
+ git reset --hard HEAD &&
+ git checkout -b test_branch HEAD~2 &&
+ echo "line 1 from test branch" >>hello &&
+ echo "line 2 from test branch" >>hello &&
+ git add hello &&
+ test_tick &&
+ git commit -m "hello: 2 more lines from a test branch" &&
+ HASH9=$(git rev-parse --verify HEAD) &&
+ git tag -s -m "tag for testing with a mergetag" test_tag HEAD &&
+ git checkout master &&
+ git merge -s ours test_tag &&
+ HASH10=$(git rev-parse --verify HEAD) &&
+ git cat-file commit $HASH10 | grep "^mergetag object"
+'
+
+test_expect_success GPG '--graft on a commit with a mergetag' '
+ test_must_fail git replace --graft $HASH10 $HASH8^1 &&
+ git replace --graft $HASH10 $HASH8^1 $HASH9 &&
+ git replace -d $HASH10
+'
+
test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 9496736a89..66643e4bd7 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -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 &&
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index e4ab0f5b64..0366653088 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1385,41 +1385,77 @@ test_expect_success 'lexical sort' '
git tag foo1.6 &&
git tag foo1.10 &&
git tag -l --sort=refname "foo*" >actual &&
- cat >expect <<EOF &&
-foo1.10
-foo1.3
-foo1.6
-EOF
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.3
+ foo1.6
+ EOF
test_cmp expect actual
'
test_expect_success 'version sort' '
git tag -l --sort=version:refname "foo*" >actual &&
- cat >expect <<EOF &&
-foo1.3
-foo1.6
-foo1.10
-EOF
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6
+ foo1.10
+ EOF
test_cmp expect actual
'
test_expect_success 'reverse version sort' '
git tag -l --sort=-version:refname "foo*" >actual &&
- cat >expect <<EOF &&
-foo1.10
-foo1.6
-foo1.3
-EOF
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.6
+ foo1.3
+ EOF
test_cmp expect actual
'
test_expect_success 'reverse lexical sort' '
git tag -l --sort=-refname "foo*" >actual &&
- cat >expect <<EOF &&
-foo1.6
-foo1.3
-foo1.10
-EOF
+ cat >expect <<-\EOF &&
+ foo1.6
+ foo1.3
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'configured lexical sort' '
+ git config tag.sort "v:refname" &&
+ git tag -l "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'option override configured sort' '
+ git tag -l --sort=-refname "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.6
+ foo1.3
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'invalid sort parameter on command line' '
+ test_must_fail git tag -l --sort=notvalid "foo*" >actual
+'
+
+test_expect_success 'invalid sort parameter in configuratoin' '
+ git config tag.sort "v:notvalid" &&
+ git tag -l "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.3
+ foo1.6
+ EOF
test_cmp expect actual
'
diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh
new file mode 100755
index 0000000000..2eda6adeb1
--- /dev/null
+++ b/t/t7112-reset-submodule.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+test_description='reset can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+test_submodule_switch "git reset --keep"
+
+test_submodule_switch "git reset --merge"
+
+test_submodule_forced_switch "git reset --hard"
+
+test_done
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 74de814aec..04118ad75b 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -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' '
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 5ddac1a9f7..474dab381a 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -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
diff --git a/t/t7613-merge-submodule.sh b/t/t7613-merge-submodule.sh
new file mode 100755
index 0000000000..d1e9fcc781
--- /dev/null
+++ b/t/t7613-merge-submodule.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='merge can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+# merges without conflicts
+test_submodule_switch "git merge"
+
+test_submodule_switch "git merge --ff"
+
+test_submodule_switch "git merge --ff-only"
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git merge --no-ff"
+
+test_done
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index 284018e3cd..021c5479bd 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -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)
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
new file mode 100755
index 0000000000..93b74867ac
--- /dev/null
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 Ephrim Khong
+#
+
+test_description='repack involving cyclic alternate'
+. ./test-lib.sh
+
+test_expect_success setup '
+ GIT_OBJECT_DIRECTORY=.git//../.git/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ touch a &&
+ git add a &&
+ git commit -m 1 &&
+ git repack -adl &&
+ echo "$(pwd)"/.git/objects/../objects >.git/objects/info/alternates
+'
+
+test_expect_success 're-packing repository with itsself as alternate' '
+ git repack -adl &&
+ git fsck
+'
+
+test_done
diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
index be802e0e16..1fc1f5f2af 100755
--- a/t/t9814-git-p4-rename.sh
+++ b/t/t9814-git-p4-rename.sh
@@ -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 &&
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2d4beb5e50..1d1c1063a3 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -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" &&
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index c617c826db..dafd6ad21a 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -489,6 +489,17 @@ test_path_is_dir () {
fi
}
+# Check if the directory exists and is empty as expected, barf otherwise.
+test_dir_is_empty () {
+ test_path_is_dir "$1" &&
+ if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
+ then
+ echo "Directory '$1' is not empty, it contains:"
+ ls -la "$1"
+ return 1
+ fi
+}
+
test_path_is_missing () {
if [ -e "$1" ]
then
@@ -542,7 +553,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 +580,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
diff --git a/t/test-lib.sh b/t/test-lib.sh
index a4795373a6..b1bc65bfb5 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -109,6 +109,10 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
export EDITOR
+# Tests using GIT_TRACE typically don't want <timestamp> <file>:<line> output
+GIT_TRACE_BARE=1
+export GIT_TRACE_BARE
+
if test -n "${TEST_GIT_INDEX_VERSION:+isset}"
then
GIT_INDEX_VERSION="$TEST_GIT_INDEX_VERSION"
diff --git a/t/valgrind/default.supp b/t/valgrind/default.supp
index 0a6724fcc4..9d51c92b74 100644
--- a/t/valgrind/default.supp
+++ b/t/valgrind/default.supp
@@ -49,3 +49,18 @@
Memcheck:Addr4
fun:copy_ref
}
+{
+ ignore-sse-check_refname_format-addr
+ Memcheck:Addr8
+ fun:check_refname_format
+}
+{
+ ignore-sse-check_refname_format-cond
+ Memcheck:Cond
+ fun:check_refname_format
+}
+{
+ ignore-sse-check_refname_format-value
+ Memcheck:Value8
+ fun:check_refname_format
+}
diff --git a/tag.c b/tag.c
index 7b07921b30..82d841bf2d 100644
--- a/tag.c
+++ b/tag.c
@@ -40,15 +40,8 @@ struct tag *lookup_tag(const unsigned char *sha1)
{
struct object *obj = lookup_object(sha1);
if (!obj)
- return create_object(sha1, OBJ_TAG, alloc_tag_node());
- if (!obj->type)
- obj->type = OBJ_TAG;
- if (obj->type != OBJ_TAG) {
- error("Object %s is a %s, not a tag",
- sha1_to_hex(sha1), typename(obj->type));
- return NULL;
- }
- return (struct tag *) obj;
+ return create_object(sha1, alloc_tag_node());
+ return object_as_type(obj, OBJ_TAG, 0);
}
static unsigned long parse_tag_date(const char *buf, const char *tail)
diff --git a/test-dump-cache-tree.c b/test-dump-cache-tree.c
index 47eab9765f..330ba4f4dd 100644
--- a/test-dump-cache-tree.c
+++ b/test-dump-cache-tree.c
@@ -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
index 0000000000..9cf3112c9d
--- /dev/null
+++ b/test-dump-split-index.c
@@ -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;
+}
diff --git a/test-hashmap.c b/test-hashmap.c
index f5183fb9e8..07aa7ecdee 100644
--- a/test-hashmap.c
+++ b/test-hashmap.c
@@ -115,9 +115,8 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
for (j = 0; j < rounds; j++) {
for (i = 0; i < TEST_SIZE; i++) {
- struct hashmap_entry key;
- hashmap_entry_init(&key, hashes[i]);
- hashmap_get(&map, &key, entries[i]->key);
+ hashmap_get_from_hash(&map, hashes[i],
+ entries[i]->key);
}
}
@@ -199,12 +198,8 @@ int main(int argc, char *argv[])
} else if (!strcmp("get", cmd) && l1) {
- /* setup static key */
- struct hashmap_entry key;
- hashmap_entry_init(&key, hash);
-
/* lookup entry in hashmap */
- entry = hashmap_get(&map, &key, p1);
+ entry = hashmap_get_from_hash(&map, hash, p1);
/* print result */
if (!entry)
@@ -239,6 +234,20 @@ int main(int argc, char *argv[])
/* print table sizes */
printf("%u %u\n", map.tablesize, map.size);
+ } else if (!strcmp("intern", cmd) && l1) {
+
+ /* test that strintern works */
+ const char *i1 = strintern(p1);
+ const char *i2 = strintern(p1);
+ if (strcmp(i1, p1))
+ printf("strintern(%s) returns %s\n", p1, i1);
+ else if (i1 == p1)
+ printf("strintern(%s) returns input pointer\n", p1);
+ else if (i1 != i2)
+ printf("strintern(%s) != strintern(%s)", i1, i2);
+ else
+ printf("%s\n", i1);
+
} else if (!strcmp("perfhashmap", cmd) && l1 && l2) {
perf_hashmap(atoi(p1), atoi(p2));
diff --git a/test-scrap-cache-tree.c b/test-scrap-cache-tree.c
index 4728013910..9ebcbca9d2 100644
--- a/test-scrap-cache-tree.c
+++ b/test-scrap-cache-tree.c
@@ -6,12 +6,11 @@ static struct lock_file index_lock;
int main(int ac, char **av)
{
- int fd = hold_locked_index(&index_lock, 1);
+ hold_locked_index(&index_lock, 1);
if (read_cache() < 0)
die("unable to read index file");
active_cache_tree = NULL;
- if (write_cache(fd, active_cache, active_nr)
- || commit_lock_file(&index_lock))
+ if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
die("unable to write index file");
return 0;
}
diff --git a/trace.c b/trace.c
index 08180a90bc..e583dc63bb 100644
--- a/trace.c
+++ b/trace.c
@@ -26,110 +26,250 @@
#include "quote.h"
/* Get a trace file descriptor from "key" env variable. */
-static int get_trace_fd(const char *key, int *need_close)
+static int get_trace_fd(struct trace_key *key)
{
- char *trace = getenv(key);
+ static struct trace_key trace_default = { "GIT_TRACE" };
+ const char *trace;
+
+ /* use default "GIT_TRACE" if NULL */
+ if (!key)
+ key = &trace_default;
+
+ /* don't open twice */
+ if (key->initialized)
+ return key->fd;
+
+ trace = getenv(key->key);
if (!trace || !strcmp(trace, "") ||
!strcmp(trace, "0") || !strcasecmp(trace, "false"))
- return 0;
- if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
- return STDERR_FILENO;
- if (strlen(trace) == 1 && isdigit(*trace))
- return atoi(trace);
- if (is_absolute_path(trace)) {
+ key->fd = 0;
+ else if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
+ key->fd = STDERR_FILENO;
+ else if (strlen(trace) == 1 && isdigit(*trace))
+ key->fd = atoi(trace);
+ else if (is_absolute_path(trace)) {
int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
if (fd == -1) {
fprintf(stderr,
"Could not open '%s' for tracing: %s\n"
"Defaulting to tracing on stderr...\n",
trace, strerror(errno));
- return STDERR_FILENO;
+ key->fd = STDERR_FILENO;
+ } else {
+ key->fd = fd;
+ key->need_close = 1;
}
- *need_close = 1;
- return fd;
+ } else {
+ fprintf(stderr, "What does '%s' for %s mean?\n"
+ "If you want to trace into a file, then please set "
+ "%s to an absolute pathname (starting with /).\n"
+ "Defaulting to tracing on stderr...\n",
+ trace, key->key, key->key);
+ key->fd = STDERR_FILENO;
}
- fprintf(stderr, "What does '%s' for %s mean?\n", trace, key);
- fprintf(stderr, "If you want to trace into a file, "
- "then please set %s to an absolute pathname "
- "(starting with /).\n", key);
- fprintf(stderr, "Defaulting to tracing on stderr...\n");
+ key->initialized = 1;
+ return key->fd;
+}
- return STDERR_FILENO;
+void trace_disable(struct trace_key *key)
+{
+ if (key->need_close)
+ close(key->fd);
+ key->fd = 0;
+ key->initialized = 1;
+ key->need_close = 0;
}
static const char err_msg[] = "Could not trace into fd given by "
"GIT_TRACE environment variable";
-static void trace_vprintf(const char *key, const char *fmt, va_list ap)
+static int prepare_trace_line(const char *file, int line,
+ struct trace_key *key, struct strbuf *buf)
{
- struct strbuf buf = STRBUF_INIT;
+ static struct trace_key trace_bare = TRACE_KEY_INIT(BARE);
+ struct timeval tv;
+ struct tm tm;
+ time_t secs;
if (!trace_want(key))
- return;
+ return 0;
set_try_to_free_routine(NULL); /* is never reset */
- strbuf_vaddf(&buf, fmt, ap);
- trace_strbuf(key, &buf);
- strbuf_release(&buf);
+
+ /* unit tests may want to disable additional trace output */
+ if (trace_want(&trace_bare))
+ return 1;
+
+ /* print current timestamp */
+ gettimeofday(&tv, NULL);
+ secs = tv.tv_sec;
+ localtime_r(&secs, &tm);
+ strbuf_addf(buf, "%02d:%02d:%02d.%06ld ", tm.tm_hour, tm.tm_min,
+ tm.tm_sec, (long) tv.tv_usec);
+
+#ifdef HAVE_VARIADIC_MACROS
+ /* print file:line */
+ strbuf_addf(buf, "%s:%d ", file, line);
+ /* align trace output (column 40 catches most files names in git) */
+ while (buf->len < 40)
+ strbuf_addch(buf, ' ');
+#endif
+
+ return 1;
+}
+
+static void print_trace_line(struct trace_key *key, struct strbuf *buf)
+{
+ /* append newline if missing */
+ if (buf->len && buf->buf[buf->len - 1] != '\n')
+ strbuf_addch(buf, '\n');
+
+ write_or_whine_pipe(get_trace_fd(key), buf->buf, buf->len, err_msg);
+ strbuf_release(buf);
+}
+
+static void trace_vprintf_fl(const char *file, int line, struct trace_key *key,
+ const char *format, va_list ap)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!prepare_trace_line(file, line, key, &buf))
+ return;
+
+ strbuf_vaddf(&buf, format, ap);
+ print_trace_line(key, &buf);
+}
+
+static void trace_argv_vprintf_fl(const char *file, int line,
+ const char **argv, const char *format,
+ va_list ap)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!prepare_trace_line(file, line, NULL, &buf))
+ return;
+
+ strbuf_vaddf(&buf, format, ap);
+
+ sq_quote_argv(&buf, argv, 0);
+ print_trace_line(NULL, &buf);
+}
+
+void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
+ const struct strbuf *data)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!prepare_trace_line(file, line, key, &buf))
+ return;
+
+ strbuf_addbuf(&buf, data);
+ print_trace_line(key, &buf);
+}
+
+static struct trace_key trace_perf_key = TRACE_KEY_INIT(PERFORMANCE);
+
+static void trace_performance_vprintf_fl(const char *file, int line,
+ uint64_t nanos, const char *format,
+ va_list ap)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!prepare_trace_line(file, line, &trace_perf_key, &buf))
+ return;
+
+ strbuf_addf(&buf, "performance: %.9f s", (double) nanos / 1000000000);
+
+ if (format && *format) {
+ strbuf_addstr(&buf, ": ");
+ strbuf_vaddf(&buf, format, ap);
+ }
+
+ print_trace_line(&trace_perf_key, &buf);
}
-__attribute__((format (printf, 2, 3)))
-void trace_printf_key(const char *key, const char *fmt, ...)
+#ifndef HAVE_VARIADIC_MACROS
+
+void trace_printf(const char *format, ...)
{
va_list ap;
- va_start(ap, fmt);
- trace_vprintf(key, fmt, ap);
+ va_start(ap, format);
+ trace_vprintf_fl(NULL, 0, NULL, format, ap);
va_end(ap);
}
-void trace_printf(const char *fmt, ...)
+void trace_printf_key(struct trace_key *key, const char *format, ...)
{
va_list ap;
- va_start(ap, fmt);
- trace_vprintf("GIT_TRACE", fmt, ap);
+ va_start(ap, format);
+ trace_vprintf_fl(NULL, 0, key, format, ap);
va_end(ap);
}
-void trace_strbuf(const char *key, const struct strbuf *buf)
+void trace_argv_printf(const char **argv, const char *format, ...)
{
- int fd, need_close = 0;
-
- fd = get_trace_fd(key, &need_close);
- if (!fd)
- return;
+ va_list ap;
+ va_start(ap, format);
+ trace_argv_vprintf_fl(NULL, 0, argv, format, ap);
+ va_end(ap);
+}
- write_or_whine_pipe(fd, buf->buf, buf->len, err_msg);
+void trace_strbuf(const char *key, const struct strbuf *data)
+{
+ trace_strbuf_fl(NULL, 0, key, data);
+}
- if (need_close)
- close(fd);
+void trace_performance(uint64_t nanos, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ trace_performance_vprintf_fl(NULL, 0, nanos, format, ap);
+ va_end(ap);
}
-void trace_argv_printf(const char **argv, const char *fmt, ...)
+void trace_performance_since(uint64_t start, const char *format, ...)
{
- struct strbuf buf = STRBUF_INIT;
va_list ap;
- int fd, need_close = 0;
+ va_start(ap, format);
+ trace_performance_vprintf_fl(NULL, 0, getnanotime() - start,
+ format, ap);
+ va_end(ap);
+}
- fd = get_trace_fd("GIT_TRACE", &need_close);
- if (!fd)
- return;
+#else
- set_try_to_free_routine(NULL); /* is never reset */
- va_start(ap, fmt);
- strbuf_vaddf(&buf, fmt, ap);
+void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ trace_vprintf_fl(file, line, key, format, ap);
va_end(ap);
+}
- sq_quote_argv(&buf, argv, 0);
- strbuf_addch(&buf, '\n');
- write_or_whine_pipe(fd, buf.buf, buf.len, err_msg);
- strbuf_release(&buf);
+void trace_argv_printf_fl(const char *file, int line, const char **argv,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ trace_argv_vprintf_fl(file, line, argv, format, ap);
+ va_end(ap);
+}
- if (need_close)
- close(fd);
+void trace_performance_fl(const char *file, int line, uint64_t nanos,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ trace_performance_vprintf_fl(file, line, nanos, format, ap);
+ va_end(ap);
}
+#endif /* HAVE_VARIADIC_MACROS */
+
+
static const char *quote_crnl(const char *path)
{
static char new_path[PATH_MAX];
@@ -156,11 +296,11 @@ static const char *quote_crnl(const char *path)
/* FIXME: move prefix to startup_info struct and get rid of this arg */
void trace_repo_setup(const char *prefix)
{
- static const char *key = "GIT_TRACE_SETUP";
+ static struct trace_key key = TRACE_KEY_INIT(SETUP);
const char *git_work_tree;
char cwd[PATH_MAX];
- if (!trace_want(key))
+ if (!trace_want(&key))
return;
if (!getcwd(cwd, PATH_MAX))
@@ -172,18 +312,117 @@ void trace_repo_setup(const char *prefix)
if (!prefix)
prefix = "(null)";
- trace_printf_key(key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
- trace_printf_key(key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
- trace_printf_key(key, "setup: cwd: %s\n", quote_crnl(cwd));
- trace_printf_key(key, "setup: prefix: %s\n", quote_crnl(prefix));
+ trace_printf_key(&key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
+ trace_printf_key(&key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
+ trace_printf_key(&key, "setup: cwd: %s\n", quote_crnl(cwd));
+ trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));
}
-int trace_want(const char *key)
+int trace_want(struct trace_key *key)
{
- const char *trace = getenv(key);
+ return !!get_trace_fd(key);
+}
- if (!trace || !strcmp(trace, "") ||
- !strcmp(trace, "0") || !strcasecmp(trace, "false"))
+#ifdef HAVE_CLOCK_GETTIME
+
+static inline uint64_t highres_nanos(void)
+{
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
return 0;
- return 1;
+ return (uint64_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
+}
+
+#elif defined (GIT_WINDOWS_NATIVE)
+
+static inline uint64_t highres_nanos(void)
+{
+ static uint64_t high_ns, scaled_low_ns;
+ static int scale;
+ LARGE_INTEGER cnt;
+
+ if (!scale) {
+ if (!QueryPerformanceFrequency(&cnt))
+ return 0;
+
+ /* high_ns = number of ns per cnt.HighPart */
+ high_ns = (1000000000LL << 32) / (uint64_t) cnt.QuadPart;
+
+ /*
+ * Number of ns per cnt.LowPart is 10^9 / frequency (or
+ * high_ns >> 32). For maximum precision, we scale this factor
+ * so that it just fits within 32 bit (i.e. won't overflow if
+ * multiplied with cnt.LowPart).
+ */
+ scaled_low_ns = high_ns;
+ scale = 32;
+ while (scaled_low_ns >= 0x100000000LL) {
+ scaled_low_ns >>= 1;
+ scale--;
+ }
+ }
+
+ /* if QPF worked on initialization, we expect QPC to work as well */
+ QueryPerformanceCounter(&cnt);
+
+ return (high_ns * cnt.HighPart) +
+ ((scaled_low_ns * cnt.LowPart) >> scale);
+}
+
+#else
+# define highres_nanos() 0
+#endif
+
+static inline uint64_t gettimeofday_nanos(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (uint64_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
+}
+
+/*
+ * Returns nanoseconds since the epoch (01/01/1970), for performance tracing
+ * (i.e. favoring high precision over wall clock time accuracy).
+ */
+inline uint64_t getnanotime(void)
+{
+ static uint64_t offset;
+ if (offset > 1) {
+ /* initialization succeeded, return offset + high res time */
+ return offset + highres_nanos();
+ } else if (offset == 1) {
+ /* initialization failed, fall back to gettimeofday */
+ return gettimeofday_nanos();
+ } else {
+ /* initialize offset if high resolution timer works */
+ uint64_t now = gettimeofday_nanos();
+ uint64_t highres = highres_nanos();
+ if (highres)
+ offset = now - highres;
+ else
+ offset = 1;
+ return now;
+ }
+}
+
+static uint64_t command_start_time;
+static struct strbuf command_line = STRBUF_INIT;
+
+static void print_command_performance_atexit(void)
+{
+ trace_performance_since(command_start_time, "git command:%s",
+ command_line.buf);
+}
+
+void trace_command_performance(const char **argv)
+{
+ if (!trace_want(&trace_perf_key))
+ return;
+
+ if (!command_start_time)
+ atexit(print_command_performance_atexit);
+
+ strbuf_reset(&command_line);
+ sq_quote_argv(&command_line, argv, 0);
+ command_start_time = getnanotime();
}
diff --git a/trace.h b/trace.h
new file mode 100644
index 0000000000..ae6a332947
--- /dev/null
+++ b/trace.h
@@ -0,0 +1,113 @@
+#ifndef TRACE_H
+#define TRACE_H
+
+#include "git-compat-util.h"
+#include "strbuf.h"
+
+struct trace_key {
+ const char * const key;
+ int fd;
+ unsigned int initialized : 1;
+ unsigned int need_close : 1;
+};
+
+#define TRACE_KEY_INIT(name) { "GIT_TRACE_" #name, 0, 0, 0 }
+
+extern void trace_repo_setup(const char *prefix);
+extern int trace_want(struct trace_key *key);
+extern void trace_disable(struct trace_key *key);
+extern uint64_t getnanotime(void);
+extern void trace_command_performance(const char **argv);
+
+#ifndef HAVE_VARIADIC_MACROS
+
+__attribute__((format (printf, 1, 2)))
+extern void trace_printf(const char *format, ...);
+
+__attribute__((format (printf, 2, 3)))
+extern void trace_printf_key(struct trace_key *key, const char *format, ...);
+
+__attribute__((format (printf, 2, 3)))
+extern void trace_argv_printf(const char **argv, const char *format, ...);
+
+extern void trace_strbuf(struct trace_key *key, const struct strbuf *data);
+
+/* Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled. */
+__attribute__((format (printf, 2, 3)))
+extern void trace_performance(uint64_t nanos, const char *format, ...);
+
+/* Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled. */
+__attribute__((format (printf, 2, 3)))
+extern void trace_performance_since(uint64_t start, const char *format, ...);
+
+#else
+
+/*
+ * Macros to add file:line - see above for C-style declarations of how these
+ * should be used.
+ */
+
+/*
+ * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The
+ * default is __FILE__, as it is consistent with assert(), and static function
+ * names are not necessarily unique.
+ *
+ * __FILE__ ":" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied
+ * by the preprocessor as a string literal, and __FUNCTION__ is filled in by
+ * the compiler as a string constant.
+ */
+#ifndef TRACE_CONTEXT
+# define TRACE_CONTEXT __FILE__
+#endif
+
+/*
+ * Note: with C99 variadic macros, __VA_ARGS__ must include the last fixed
+ * parameter ('format' in this case). Otherwise, a call without variable
+ * arguments will have a surplus ','. E.g.:
+ *
+ * #define foo(format, ...) bar(format, __VA_ARGS__)
+ * foo("test");
+ *
+ * will expand to
+ *
+ * bar("test",);
+ *
+ * which is invalid (note the ',)'). With GNUC, '##__VA_ARGS__' drops the
+ * comma, but this is non-standard.
+ */
+
+#define trace_printf(...) \
+ trace_printf_key_fl(TRACE_CONTEXT, __LINE__, NULL, __VA_ARGS__)
+
+#define trace_printf_key(key, ...) \
+ trace_printf_key_fl(TRACE_CONTEXT, __LINE__, key, __VA_ARGS__)
+
+#define trace_argv_printf(argv, ...) \
+ trace_argv_printf_fl(TRACE_CONTEXT, __LINE__, argv, __VA_ARGS__)
+
+#define trace_strbuf(key, data) \
+ trace_strbuf_fl(TRACE_CONTEXT, __LINE__, key, data)
+
+#define trace_performance(nanos, ...) \
+ trace_performance_fl(TRACE_CONTEXT, __LINE__, nanos, __VA_ARGS__)
+
+#define trace_performance_since(start, ...) \
+ trace_performance_fl(TRACE_CONTEXT, __LINE__, getnanotime() - (start), \
+ __VA_ARGS__)
+
+/* backend functions, use non-*fl macros instead */
+__attribute__((format (printf, 4, 5)))
+extern void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
+ const char *format, ...);
+__attribute__((format (printf, 4, 5)))
+extern void trace_argv_printf_fl(const char *file, int line, const char **argv,
+ const char *format, ...);
+extern void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
+ const struct strbuf *data);
+__attribute__((format (printf, 4, 5)))
+extern void trace_performance_fl(const char *file, int line,
+ uint64_t nanos, const char *fmt, ...);
+
+#endif /* HAVE_VARIADIC_MACROS */
+
+#endif /* TRACE_H */
diff --git a/transport-helper.c b/transport-helper.c
index 84c616f180..3d8fe7d801 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -153,7 +153,7 @@ static struct child_process *get_helper(struct transport *transport)
write_constant(helper->in, "capabilities\n");
while (1) {
- const char *capname;
+ const char *capname, *arg;
int mandatory = 0;
if (recvline(data, &buf))
exit(128);
@@ -183,19 +183,19 @@ static struct child_process *get_helper(struct transport *transport)
data->export = 1;
else if (!strcmp(capname, "check-connectivity"))
data->check_connectivity = 1;
- else if (!data->refspecs && starts_with(capname, "refspec ")) {
+ else if (!data->refspecs && skip_prefix(capname, "refspec ", &arg)) {
ALLOC_GROW(refspecs,
refspec_nr + 1,
refspec_alloc);
- refspecs[refspec_nr++] = xstrdup(capname + strlen("refspec "));
+ refspecs[refspec_nr++] = xstrdup(arg);
} else if (!strcmp(capname, "connect")) {
data->connect = 1;
} else if (!strcmp(capname, "signed-tags")) {
data->signed_tags = 1;
- } else if (starts_with(capname, "export-marks ")) {
- data->export_marks = xstrdup(capname + strlen("export-marks "));
- } else if (starts_with(capname, "import-marks")) {
- data->import_marks = xstrdup(capname + strlen("import-marks "));
+ } else if (skip_prefix(capname, "export-marks ", &arg)) {
+ data->export_marks = xstrdup(arg);
+ } else if (skip_prefix(capname, "import-marks ", &arg)) {
+ data->import_marks = xstrdup(arg);
} else if (starts_with(capname, "no-private-update")) {
data->no_private_update = 1;
} else if (mandatory) {
diff --git a/transport.c b/transport.c
index 325f03e1ee..80ed1262c2 100644
--- a/transport.c
+++ b/transport.c
@@ -192,7 +192,9 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
static const char *rsync_url(const char *url)
{
- return !starts_with(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
+ if (!starts_with(url, "rsync://"))
+ skip_prefix(url, "rsync:", &url);
+ return url;
}
static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
@@ -261,32 +263,20 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
static int fetch_objs_via_rsync(struct transport *transport,
int nr_objs, struct ref **to_fetch)
{
- struct strbuf buf = STRBUF_INIT;
struct child_process rsync;
- const char *args[8];
- int result;
-
- strbuf_addstr(&buf, rsync_url(transport->url));
- strbuf_addstr(&buf, "/objects/");
memset(&rsync, 0, sizeof(rsync));
- rsync.argv = args;
rsync.stdout_to_stderr = 1;
- args[0] = "rsync";
- args[1] = (transport->verbose > 1) ? "-rv" : "-r";
- args[2] = "--ignore-existing";
- args[3] = "--exclude";
- args[4] = "info";
- args[5] = buf.buf;
- args[6] = get_object_directory();
- args[7] = NULL;
+ argv_array_push(&rsync.args, "rsync");
+ argv_array_push(&rsync.args, (transport->verbose > 1) ? "-rv" : "-r");
+ argv_array_push(&rsync.args, "--ignore-existing");
+ argv_array_push(&rsync.args, "--exclude");
+ argv_array_push(&rsync.args, "info");
+ argv_array_pushf(&rsync.args, "%s/objects/", rsync_url(transport->url));
+ argv_array_push(&rsync.args, get_object_directory());
/* NEEDSWORK: handle one level of alternates */
- result = run_command(&rsync);
-
- strbuf_release(&buf);
-
- return result;
+ return run_command(&rsync);
}
static int write_one_ref(const char *name, const unsigned char *sha1,
@@ -1186,10 +1176,8 @@ int transport_push(struct transport *transport,
if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
struct ref *ref = remote_refs;
- struct string_list needs_pushing;
+ struct string_list needs_pushing = STRING_LIST_INIT_DUP;
- memset(&needs_pushing, 0, sizeof(struct string_list));
- needs_pushing.strdup_strings = 1;
for (; ref; ref = ref->next)
if (!is_null_sha1(ref->new_sha1) &&
find_unpushed_submodules(ref->new_sha1,
diff --git a/tree-walk.c b/tree-walk.c
index 4dc86c7fe5..5dd9a71804 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -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)
{
/*
diff --git a/tree.c b/tree.c
index c8c49d7b78..bb02c1caa4 100644
--- a/tree.c
+++ b/tree.c
@@ -183,15 +183,8 @@ struct tree *lookup_tree(const unsigned char *sha1)
{
struct object *obj = lookup_object(sha1);
if (!obj)
- return create_object(sha1, OBJ_TREE, alloc_tree_node());
- if (!obj->type)
- obj->type = OBJ_TREE;
- if (obj->type != OBJ_TREE) {
- error("Object %s is a %s, not a tree",
- sha1_to_hex(sha1), typename(obj->type));
- return NULL;
- }
- return (struct tree *) obj;
+ return create_object(sha1, alloc_tree_node());
+ return object_as_type(obj, OBJ_TREE, 0);
}
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
diff --git a/unicode_width.h b/unicode_width.h
index 4db78038e4..47cdd2369d 100644
--- a/unicode_width.h
+++ b/unicode_width.h
@@ -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 }
diff --git a/unix-socket.c b/unix-socket.c
index 01f119f970..91bd6b89d4 100644
--- a/unix-socket.c
+++ b/unix-socket.c
@@ -99,11 +99,12 @@ int unix_stream_listen(const char *path)
struct sockaddr_un sa;
struct unix_sockaddr_context ctx;
+ unlink(path);
+
if (unix_sockaddr_init(&sa, path, &ctx) < 0)
return -1;
fd = unix_stream_socket();
- unlink(path);
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
goto fail;
diff --git a/unpack-trees.c b/unpack-trees.c
index 97fc995467..c6aa8fb993 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -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);
}
/*
diff --git a/url.c b/url.c
index 335d97d3f7..7ca2a69e10 100644
--- a/url.c
+++ b/url.c
@@ -121,7 +121,7 @@ void end_url_with_slash(struct strbuf *buf, const char *url)
{
strbuf_addstr(buf, url);
if (buf->len && buf->buf[buf->len - 1] != '/')
- strbuf_addstr(buf, "/");
+ strbuf_addch(buf, '/');
}
void str_end_url_with_slash(const char *url, char **dest) {
diff --git a/urlmatch.c b/urlmatch.c
index ec87cba750..3d4c54b5cd 100644
--- a/urlmatch.c
+++ b/urlmatch.c
@@ -483,8 +483,7 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
int user_matched = 0;
int retval;
- key = skip_prefix(var, collect->section);
- if (!key || *(key++) != '.') {
+ if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') {
if (collect->cascade_fn)
return collect->cascade_fn(var, value, cb);
return 0; /* not interested */
diff --git a/walker.c b/walker.c
index 1dd86b8f33..014826464e 100644
--- 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 @@ unlock_and_fail:
for (i = 0; i < targets; i++)
if (lock[i])
unlock_ref(lock[i]);
+ free(to_free);
return -1;
}
diff --git a/wt-status.c b/wt-status.c
index 318a191238..27da5296be 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -574,14 +574,11 @@ static void wt_status_collect_untracked(struct wt_status *s)
{
int i;
struct dir_struct dir;
- struct timeval t_begin;
+ uint64_t t_begin = getnanotime();
if (!s->show_untracked_files)
return;
- if (advice_status_u_option)
- gettimeofday(&t_begin, NULL);
-
memset(&dir, 0, sizeof(dir));
if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
dir.flags |=
@@ -612,13 +609,8 @@ static void wt_status_collect_untracked(struct wt_status *s)
free(dir.ignored);
clear_directory(&dir);
- if (advice_status_u_option) {
- struct timeval t_end;
- gettimeofday(&t_end, NULL);
- s->untracked_in_ms =
- (uint64_t)t_end.tv_sec * 1000 + t_end.tv_usec / 1000 -
- ((uint64_t)t_begin.tv_sec * 1000 + t_begin.tv_usec / 1000);
- }
+ if (advice_status_u_option)
+ s->untracked_in_ms = (getnanotime() - t_begin) / 1000000;
}
void wt_status_collect(struct wt_status *s)
@@ -734,37 +726,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);
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 9e13b25abc..625198e058 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -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;